riok / mapperly

A .NET source generator for generating object mappings. No runtime reflection.
https://mapperly.riok.app
Apache License 2.0
2.52k stars 138 forks source link

Generator 'MapperGenerator' failed to generate source. It will not contribute to the output and compilation errors may occur as a result. #1403

Closed alexandrucatalinene closed 1 month ago

alexandrucatalinene commented 1 month ago

Describe the bug A clear and concise description of what the bug is. I have a very simple mapping made from a model to a dto but it seems it doesn't work. I get this error in the console (Visual Studio 2022)

Severity    Code    Description Project File    Line    Suppression State
Warning (active)    CS8785  Generator 'MapperGenerator' failed to generate source. It will not contribute to the output and compilation errors may occur as a result. Exception was of type 'InvalidOperationException' with message 'Collection was modified; enumeration operation may not execute.'. Api Path\CSC    1   

The code gets generated but it's not included in the build so it gives an error.

Declaration code

// Relevant code of the Mapperly definition and all relevant classes
[Mapper]
public static partial class UserMapper
{
    public static partial User MapDtoToDb(UserDTO user);
    public static partial UserDTO MapDbToDto(User user);
}

public class UserDTO
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public string? DeviceId { get; set; }
    public string? Password { get; set; }
}

Actual relevant generated code

// Actual relevant code generated by Mapperly
// see also https://mapperly.riok.app/docs/configuration/generated-source/

Expected relevant generated code

// The generated code how you expect it to look like
public static partial class UserMapper
{
    [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.0.0.0")]
    public static global::Api.Database.Models.User MapDtoToDb(global::Api.DTO.UserDTO user)
    {
        var target = new global::Api.Database.Models.User()
        {
            Name = user.Name ?? "",
            DeviceId = user.DeviceId ?? "",
            Password = user.Password ?? "",
        };
        target.Id = user.Id;
        return target;
    }

    [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "4.0.0.0")]
    public static global::Api.DTO.UserDTO MapDbToDto(global::Api.Database.Models.User user)
    {
        var target = new global::Api.DTO.UserDTO();
        target.Id = user.Id;
        target.Name = user.Name;
        target.DeviceId = user.DeviceId;
        target.Password = user.Password;
        return target;
    }
}

Environment (please complete the following information):

Additional context Simple dotnet minimap API project.

latonz commented 1 month ago

Thanks for reporting. Could you post the source of your User class?

alexandrucatalinene commented 1 month ago

Sure. Sorry for the late reply

public class User
{
    public int Id { get; set; }
    public required string Name { get; set; }
    public required string DeviceId { get; set; }
    public required string Password { get; set; }
}
alexandrucatalinene commented 1 month ago

I also managed to obtained a stacktrace of the error

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.Collections.Generic.HashSet`1.Enumerator.MoveNext()
   at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
   at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
   at System.Linq.Enumerable.<DistinctIterator>d__64`1.MoveNext()
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
   at Riok.Mapperly.Descriptors.MappingBodyBuilders.ObjectMemberMappingBodyBuilder.BuildMappingBody(IMembersContainerBuilderContext`1 ctx) in /_/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/ObjectMemberMappingBodyBuilder.cs:line 30
   at Riok.Mapperly.Descriptors.MappingBodyBuilders.NewInstanceObjectMemberMappingBodyBuilder.BuildMappingBody(MappingBuilderContext ctx, NewInstanceObjectMemberMethodMapping mapping) in /_/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/NewInstanceObjectMemberMappingBodyBuilder.cs:line 36
   at Riok.Mapperly.Descriptors.MappingBodyBuilders.MappingBodyBuilder.BuildMappingBodies(CancellationToken cancellationToken) in /_/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/MappingBodyBuilder.cs:line 27
   at Riok.Mapperly.Descriptors.DescriptorBuilder.Build(CancellationToken cancellationToken) in /_/src/Riok.Mapperly/Descriptors/DescriptorBuilder.cs:line 84
   at Riok.Mapperly.MapperGenerator.BuildDescriptor(CompilationContext compilationContext, MapperDeclaration mapperDeclaration, MapperConfiguration mapperDefaults, CancellationToken cancellationToken) in /_/src/Riok.Mapperly/MapperGenerator.cs:line 80
   at Riok.Mapperly.MapperGenerator.<>c.<Initialize>b__2_3(ValueTuple`2 x, CancellationToken ct) in /_/src/Riok.Mapperly/MapperGenerator.cs:line 48
   at Microsoft.CodeAnalysis.UserFunctionExtensions.<>c__DisplayClass0_0`2.<WrapUserFunction>b__0(TInput input, CancellationToken token)
latonz commented 1 month ago

I cannot reproduce this, do you have any additional configuration? Any mapperly related attributes or properties (e.g. MapperlyDefaults)? Could you share a minimal repro as a GitHub repo/gist?

alexandrucatalinene commented 1 month ago

Yes I have

[assembly: MapperDefaults(EnumMappingIgnoreCase = true, ThrowOnMappingNullMismatch = false)]

but even when I comment this part it still errors out.

Also, I tried on two different computers with a similar setup and it does the same.

It could be something I have configured but I don't know what could it be.

I attached an empty Web Api project with Mapperly that reproduces the errors for me.

MapperlyReproduce.zip

latonz commented 1 month ago

Mhmm I still cannot reproduce it, the projects builds perfectly fine on my machine (unfortunately I don't have a windows machine by hand and therefore no visual studio). Can you build the docker container? Can you build the project on the CLI?

alexandrucatalinene commented 1 month ago

Update. I tried it on the same machine but with Rider 2024.1.2 and it works.

latonz commented 1 month ago

Does it work on the cli and in docker?

alexandrucatalinene commented 1 month ago

It works with cli - didn't try it with docker but it's probably the same since it's also cli.

So, this leads me to believe it has something to do with VS

latonz commented 1 month ago

Unfortunately I don‘t have easy access to a windows machine and VS, and therefore cannot reproduce this. Maybe one of the previous comitters with a win machine could look into this @CommonGuy @TimothyMakkison @trejjam 😊

trejjam commented 1 month ago

I can try to take a look

trejjam commented 1 month ago

I was able to reproduce the issue. It's happening only in VS (tested VS 2022, CLI .NET 8, Rider 2024.1.4).

(screenshots are from Rider, but it's attached debugger from VS)

The state after retrieving the first item from EnumerateUnmappedOrConfiguredTargetMembers in the ObjectMemberMappingBodyBuilder.BuildMappingBody: Mapperly-VS_init

The state changes after BuildMemberAssignmentMapping (Id is removed from _unmappedTargetMemberNames): Mapperly-VS_after-BuildMemberAssignmentMapping

Exception from the HashSet.Enumerator.MoveNext: Mapperly-VS_hash-set-next-exception

trejjam commented 1 month ago

PR fixing the issue: #1438