Skip to content

Conversation

@DocSvartz
Copy link
Contributor

@andrerav @stagep @mokarchi it is basically possible :)

  1. The only remaining questions are about migration to ITypeAdapterConfig for
    1.1) Imapper
    1.2) IRegister
    1.3) Mapster tool
    1.4) maybe something else

  2. ITypeAdapterConfig methods count

  3. Regarding the warning This may have already been fixed.
    It's either related to concurrent access (I think there was an issue on this topic) or related to dependent types.

  4. I'm sure I've forgotten something else :)

add FrozenTypeAdapterConfig
@DocSvartz DocSvartz marked this pull request as ready for review October 11, 2025 07:01
@DocSvartz DocSvartz marked this pull request as draft October 11, 2025 07:01
@mokarchi
Copy link
Contributor

Migrating to ITypeAdapterConfig means updating IMapper to work with a more generic interface, allowing flexibility for different implementations (e.g., FrozenTypeAdapterConfig). For migrating IMapper to ITypeAdapterConfig, we can update ServiceMapper to accept ITypeAdapterConfig instead of TypeAdapterConfig

Migrating to ITypeAdapterConfig requires updating the Register method to accept ITypeAdapterConfig, allowing modules to work with any implementation, such as FrozenTypeAdapterConfig.The main challenge is ensuring immutability for frozen configurations.

@mokarchi
Copy link
Contributor

Your implementation of FrozenTypeAdapterConfig as a decorator is a clean and extensible approach to adding freezing support. The use of ConcurrentDictionary for thread-safety and the flexibility of FrozenTypes and DeepFreeze are excellent additions. I’ve reviewed the code and have a few suggestions to refine the implementation.

Prevent Apply and Scan from modifying the config after DeepFreeze or FrozenTypes:

public override void Apply(IEnumerable<IRegister> registers)
{
    if (IsTotalFrozen)
        throw new InvalidOperationException("Cannot apply registers after DeepFreeze.");
    foreach (var type in _frozentypes.Keys)
        if (registers.Any(r => r.GetType().GetTypeInfo().GetInterfaces().Any(i => i == typeof(IRegister))))
            throw new InvalidOperationException($"Cannot apply registers for frozen type pair {type}.");
    base.Apply(registers);
}

Instead of returning a cleared _dummyConfig, throw an InvalidOperationException to make the behavior explicit:

public override TypeAdapterSetter<TSource, TDestination> NewConfig<TSource, TDestination>()
{
    if (IsTotalFrozen || _frozentypes.ContainsKey(new TypeTuple(typeof(TSource), typeof(TDestination))))
        throw new InvalidOperationException("Cannot modify frozen configuration.");
    return base.NewConfig<TSource, TDestination>();
}

@DocSvartz
Copy link
Contributor Author

@mokarchi

Prevent Apply and Scan from modifying the config after DeepFreeze or FrozenTypes:

It is already ensures not modify.
To achieve this, two conditions must be met:

  1. Scan() must call your overloaded Apply()
  2. Apply() must pass a instance of Frozenconfig to IRegister

Instead of returning a cleared _dummyConfig, throw an InvalidOperationException to make the behavior explicit:

I'm currently testing the implicit approach to identify additional issues.

@DocSvartz
Copy link
Contributor Author

  1. ITypeAdapterConfig methods count

The interface can be simplified to the following form:

public interface ITypeAdapterConfig
{
    bool AllowImplicitDestinationInheritance { get; set; }
    bool AllowImplicitSourceInheritance { get; set; }
    Func<LambdaExpression, Delegate> Compiler { get; set; } 
    ConfigCompileStorage ConfigCompile { get; } // All internal methods related to obtaining the mapping function have been moved here.
    bool IsGlobalSettings { get; } // static GlobalSettings moved to a separate class
    bool RequireDestinationMemberSource { get; set; }
    bool RequireExplicitMapping { get; set; }
    bool RequireExplicitMappingPrimitive { get; set; }
    ConcurrentDictionary<TypeTuple, TypeAdapterRule> RuleMap { get; }
    List<TypeAdapterRule> Rules { get; }
    bool SelfContainedCodeGeneration { get; set; }

    void Apply(IEnumerable<IRegister> registers);
    void Clear();
    ITypeAdapterConfig Clone();
    void Compile(bool failFast = true);
    void Compile(Type sourceType, Type destinationType);
    void CompileProjection();
    void CompileProjection(Type sourceType, Type destinationType);
    LambdaExpression CreateMapExpression(TypeTuple tuple, MapType mapType);
    ITypeAdapterConfig Fork(Action<ITypeAdapterConfig> action, [CallerFilePath] string key1 = "", [CallerLineNumber] int key2 = 0);
    TypeAdapterSetter ForType(Type sourceType, Type destinationType);
    TypeAdapterSetter<TSource, TDestination> ForType<TSource, TDestination>();
    void Remove(Type sourceType, Type destinationType);
}

The remaining methods have been converted to Extensions methods.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants