Search code examples
c#listautomapperienumerableprotobuf-csharp-port

Automapper IEnumerable within class is not being mapped to RepeatedField


I want to map between two classes:

public class A {
    public IEnumerable<C> someList
}

and

public class B {
    public RepeatedField<D> someList
}

where RepeatedField is a class from Google.Protobuf.Collections that handles gRPC data.

EDIT: As it turns out, the way that gRPC creates classes via its prototype is not exactly like creating a class like B. See my answer.

I create an Automapper MappingConfiguration like this

return new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<C, D>().ReverseMap();
        cfg.CreateMap<A, B>().ReverseMap();
    });

and then it gets registered via ASP.NET Startup class.

If I do something like this in another class

A instanceA; // assume A's list has values inside
var listofD = this.mapper.Map<List<D>>(A.someList)

it correctly returns a list with values inside. However:

A instanceA; // assume A's list has values inside
B instanceB = this.mapper.Map<B>(A);

returns an instance of B, but the list inside of instanceB is empty. How do I fix this?


Solution

  • I've solved the issue.

    A Google.Protobuf.Collections.RepeatedField inside a C# class is readonly, meaning that directly assigning values into it won't work and will only return an empty list on the way back. Therefore, I created a custom type converter between the two larger classes to bring them together. What it does is add values directly into the RepeatedField rather than populating my own RepeatedField and assigning the value into the class.

    public static class mapConfig
    {
        public static ContainerBuilder RegisterObjectMappers(this ContainerBuilder builder)
        {
            builder.Register(c => GetV1MapperConfiguration().CreateMapper())
                .As<IMapper>().SingleInstance();
    
            return builder;
        }
        private static MapperConfiguration GetMapConfig()
        {
            return new MapperConfiguration(cfg =>
            {
                // some mappings here
                cfg.CreateMap<C, D>().ReverseMap();
                cfg.CreateMap<A, B>().ConvertUsing<AToBConverter>();
            });
        }
    }
    public class AToBConverter : ITypeConverter<A, B>
    {
        public B Convert(A source, B destination, ResolutionContext context)
        {
            var b = new B
            {
                // internal values here aside from the repeated field(s)
            };
    
            // Need to use the Add method to add values rather than assign it with an '=' sign
            foreach (var someValue in source.someList)
            {
                b.someList.Add(context.Mapper.Map<D>(someValue));
            }
            return b;
        }
    }
    

    Converting from RepeatedField to a List or any other IEnumerable in a mapped class isn't any trouble and didn't require another converter for me.