4

我想在两个类之间映射:

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 Startup 类注册。

如果我在另一个班级做这样的事情

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 内部的列表为空。我该如何解决?

4

2 回答 2

6

您需要创建一个自定义类型转换器来执行转换:

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<,>));

在线尝试

于 2019-06-25T01:07:02.020 回答
-1

我已经解决了这个问题。

C# 类中的 Google.Protobuf.Collections.RepeatedField 是只读的,这意味着直接将值分配给它是行不通的,只会在返回的路上返回一个空列表。因此,我在两个较大的类之间创建了一个自定义类型转换器以将它们组合在一起。它所做的是将值直接添加到 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 没有任何问题,并且我不需要另一个转换器。

于 2019-06-25T22:25:03.820 回答