Automapper IEnumerable within class is not being mapped to RepeatedField

拈花ヽ惹草 提交于 2019-12-02 15:34:19

问题


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?


回答1:


You need to create a custom type converter for performing the conversion:

private class EnumerableToRepeatedFieldTypeConverter<TITemSource, TITemDest> : ITypeConverter<IEnumerable<TITemSource>, RepeatedField<TITemDest>>
{
    public RepeatedField<TITemDest> Convert(IEnumerable<TITemSource> source, RepeatedField<TITemDest> destination, ResolutionContext context)
    {
        destination = destination ?? new RepeatedField<TITemDest>();
        foreach (var item in source)
        {
            // obviously we haven't performed the mapping for the item yet
            // since AutoMapper didn't recognise the list conversion
            // so we need to map the item here and then add it to the new
            // collection
            destination.Add(context.Mapper.Map<TITemDest>(item));
        }
        return destination;
    }
}

And the other way, if required:

private class RepeatedFieldToListTypeConverter<TITemSource, TITemDest> : ITypeConverter<RepeatedField<TITemSource>, List<TITemDest>>
{
    public List<TITemDest> Convert(RepeatedField<TITemSource> source, List<TITemDest> destination, ResolutionContext context)
    {
        destination = destination ?? new List<TITemDest>();
        foreach (var item in source)
        {
            destination.Add(context.Mapper.Map<TITemDest>(item));
        }
        return destination;
    }
}

Which you can register like so:

ce.CreateMap(typeof(IEnumerable<>), typeof(RepeatedField<>)).ConvertUsing(typeof(EnumerableToRepeatedFieldTypeConverter<,>));
ce.CreateMap(typeof(RepeatedField<>), typeof(List<>)).ConvertUsing(typeof(RepeatedFieldToListTypeConverter<,>));

Try it online




回答2:


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.



来源:https://stackoverflow.com/questions/56745327/automapper-ienumerable-within-class-is-not-being-mapped-to-repeatedfield

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!