Skip to content

TypeAdapterConfig.GlobalSettings.Compile() throws exception when open generics are configured #925

Closed

Description

@cjung95

When a mapping for a generic type is configured like described here TypeAdapterConfig.GlobalSettings.Compile() throws a Mapster.CompileException: 'Operation is not valid due to the current state of the object.' with an inner ArgumentException: Type ClassA`1[T] is a generic type definition (Parameter 'type').

Minimal working example:

using Mapster;

TypeAdapterConfig.GlobalSettings.ForType(typeof(ClassA<>), typeof(ClassB<>));
TypeAdapterConfig.GlobalSettings.Compile();

var classA = new ClassA<int> { Variable = 15 };
var classB = classA.Adapt<ClassB<int>>();

class ClassA<T> { public T? Variable { get; set; } = default; }
class ClassB<T> { public T? Variable { get; set; } = default; }

When removing TypeAdapterConfig.GlobalSettings.Compile(); the mapping works just fine.

The exception is thrown in Mapster.Adapters.BaseAdapter.CreateAdaptFunc(CompileArgument arg) when calling Expression.Parameter(arg.SourceType).

Mapster version 10.0.7

Activity

  1. DocSvartz commented on Apr 24, 2026

    @DocSvartz
    Contributor

    Hi @cjung95 I checked, this isn't a regression in v10. It also threw an error in version 7.4.0.
    It seems to actually work like a hidden Inherits - when a child class copies the parent's settings. But the OpenGeneric configuration itself ends up in the shared settings store.
    And since it's impossible to create an OpenGeneric instance, it doesn't compile, as it's impossible to create a function that instantiates OpenGeneric.

  2. cjung95 commented on Apr 24, 2026

    @cjung95
    Author

    Hi @DocSvartz,
    thanks for you reply.
    I'm running the Compile() function to validate my configuration, so I need a solution here.
    Should I figure out a solution myself and create a pull request, or will someone on your team take on this issue?

  3. DocSvartz commented on Apr 24, 2026

    @DocSvartz
    Contributor

    I'm currently working on a new way to create config for OpenGeneric
    using marker

    TypeAdapterConfig<ClassA<IOpenGeneric>, ClassB<IOpenGeneric>>
    .NewConfig()
    .Map(dest => dest.Variable2, src => src.Variable);
    

    This problem won't occur there.

    In your case, can remove the error by removing the check for Opengeneric types.
    Because the check result is actually valid. 😅

  4. cjung95 commented on Apr 26, 2026

    @cjung95
    Author

    I don't quite follow your argument. Registering open generic types is valid according to the specification. However, when you run the function provided in the specification to validate the configuration, it throws an exception. Yet you say that the validation result is correct. Could you please explain that to me?

  5. DocSvartz commented on Apr 26, 2026

    @DocSvartz
    Contributor

    Compile is the creation of a function to convert a source type to a destination type.
    By calling Compile, you're actually trying to do the following:

    var result = new ClassB<>() // - Which is an invalid operation

    This classA.Adapt<ClassB<int>>(); in turn means:
    Convert ClassA<int> to ClassB<int> by copying the mapping settings for the types ClassA<> to ClassB<> if it exist

    var result = new ClassB<int>() // which is now a valid operation

  6. cjung95 commented on Apr 26, 2026

    @cjung95
    Author

    Then the logical conclusion would be to generally exclude open generic types from the Compile function, otherwise, the documentary is incorrect.
    Do you disagree?

    // TypeAdapterConfig.cs
    foreach (var key in keys)
    {
        try
        {
            if (key.Source == typeof(void))
                continue;
    
            // Added
            if (key.Source.ContainsGenericParameters)
                continue;
    
            _mapDict[key] = Compiler(CreateMapExpression(key, MapType.Map));
            _mapToTargetDict[key] = Compiler(CreateMapExpression(key, MapType.MapToTarget));
        }
        catch (Exception ex)
        {
            if (failFast)
            {
                throw;
            }
    
            exceptions.Add(ex);
        }
    }
  7. DocSvartz commented on Apr 26, 2026

    @DocSvartz
    Contributor

    Then the logical conclusion would be to generally exclude open generic types from the Compile function, otherwise, the documentary is incorrect.

    Yes, this is the fastest solution. But in this case, there won't be any validation at all.
    As far as I understand, the validation is run mainly to ensure that all fields are mapped.

  8. DocSvartz commented on Apr 26, 2026

    @DocSvartz
    Contributor

    @cjung95 I can add this disabling of the check as a temporary solution.
    But really, we need to come up with a proper, permanent solution 😒

  9. cjung95 commented on Apr 26, 2026

    @cjung95
    Author

    If that's okay with you, I'll take a closer look at this issue next week and find a solution that allows for proper validation even with open generics. Let's not make any hasty changes here.

  10. DocSvartz commented on Apr 26, 2026

    @DocSvartz
    Contributor

    @cjung95 Ok,

    solution using the OpenGeneric definition via proxy stub

    TypeAdapterConfig<ClassA<IOpenGeneric>, ClassB<IOpenGeneric>>
    .NewConfig()
    .Map(dest => dest.Variable2, src => src.Variable);
    

    solves this problem. However, Include() doesn't work correctly yet. 😅

  11. added 2 commits that reference this issue on Apr 28, 2026
    42f3908
    5444cfa
  12. added a commit that references this issue on Apr 28, 2026
  13. DocSvartz commented on Apr 28, 2026

    @DocSvartz
    Contributor

    Hi @cjung95. This should be fixed in https://www.nuget.org/packages/Mapster/10.0.8-pre04

    But this is only for the basic case, when the generic has one parameter.

  14. added a commit that references this issue on Apr 28, 2026
    c0ecc0f
  15. cjung95 commented on Apr 29, 2026

    @cjung95
    Author

    Hi @DocSvartz,

    your implementation idea is very good, but unfortunately it doesn't work in my case.
    I have a constraint for the generic parameter, so I get the following exception:
    System.ArgumentException: 'GenericArguments[0], 'System.Object', on 'PaginatedList`1[T]' violates the constraint of type 'T'.'
    I recommend reverting the change until we have a comprehensive solution.

  16. DocSvartz commented on Apr 29, 2026

    @DocSvartz
    Contributor

    Yes, This is another limitation, in addition to multiple generics OpenGeneric<T1,T2,T3>.
    I think we could try to get restrictions for generics and build a stub based on them. 🤔

  17. added a commit that references this issue on Apr 30, 2026
    4cef142
  18. DocSvartz commented on May 5, 2026

    @DocSvartz
    Contributor

    your implementation idea is very good, but unfortunately it doesn't work in my case.
    I have a constraint for the generic parameter, so I get the following exception:
    System.ArgumentException: 'GenericArguments[0], 'System.Object', on 'PaginatedList`1[T]' violates the constraint of type 'T'.'
    I recommend reverting the change until we have a comprehensive solution.

    @cjung95 Yes, it seems that given the extensive capabilities for setting restrictions for generic parameters, it will be necessary to write something similar to a mock library. 😅

    Compile checking for open generics will be disabled.
    However, you can always test how field mapping works by registering a stub yourself.

    config
        .NewConfig(typeof(A<>), typeof(B<>))
        .Map("BProperty", "AProperty");
    
    config
        .NewConfig<A<int>, B<string>>(); // stub for check Property mapping
    
    config.Compile(); // is not throw exception
    
  19. cjung95 commented on May 6, 2026

    @cjung95
    Author

    You're probably right about that.
    For now skipping the check for generic types solves my problem.
    I'll still give it some thought and see if there is an easier way.
    Thank you for your help!

  20. added 2 commits that reference this issue on May 6, 2026
    e8d12a7
    d72876e
  21. added 2 commits that reference this issue on Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions