Automapper IEnumerable внутри класса не отображается на RepeatedField - PullRequest
0 голосов
/ 25 июня 2019

Я хочу сопоставить два класса:

public class A {
    public IEnumerable<C> someList
}

и

public class B {
    public RepeatedField<D> someList
}

, где RepeatedField - это класс из Google.Protobuf.Collections, который обрабатывает данные gRPC.

РЕДАКТИРОВАТЬ: Как выясняется, способ, которым gRPC создает классы с помощью своего прототипа, совсем не похож на создание класса, подобного B. Смотрите мой ответ.

Я создаю конфигурацию Automapper MappingConfiguration так:

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

и затем он регистрируется через класс запуска ASP.NET.

Если я делаю что-то подобное в другом классе

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

, он корректно возвращает список со значениями внутри.Однако:

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

возвращает экземпляр B, но список внутри instanceB пуст.Как мне это исправить?

Ответы [ 2 ]

1 голос
/ 25 июня 2019

Вам необходимо создать пользовательский преобразователь типов для выполнения преобразования:

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;
    }
}

И другой способ, если требуется:

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;
    }
}

Какой выможете зарегистрироваться так:

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

Попробуйте онлайн

0 голосов
/ 26 июня 2019

Я решил проблему.

Google.Protobuf.Collections.RepeatedField внутри класса C # доступен только для чтения, что означает, что непосредственное присвоение ему значений не будет работать и будет возвращать только пустой список впуть назад.Поэтому я создал специальный преобразователь типов между двумя большими классами, чтобы объединить их.Что он делает, это добавляет значения непосредственно в RepeatedField, а не заполняет мой собственный RepeatedField и присваивает значение в класс.

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;
    }
}

Преобразование из RepeatedField в List или любой другой IEnumerable в сопоставленном классе неникаких проблем, и мне не понадобился другой конвертер.

...