class 内的自动映射器 IEnumerable 未映射到 RepeatedField
Automapper IEnumerable within class is not being mapped to RepeatedField
我想在两个 class 之间映射:
public class A {
public IEnumerable<C> someList
}
和
public class B {
public RepeatedField<D> someList
}
其中 RepeatedField 是 Google.Protobuf.Collections 中处理 gRPC 数据的 class。
编辑:事实证明,gRPC 通过其原型创建 classes 的方式并不完全像创建像 B 这样的 class。请参阅我的回答。
我像这样创建一个 Automapper MappingConfiguration
return new MapperConfiguration(cfg =>
{
cfg.CreateMap<C, D>().ReverseMap();
cfg.CreateMap<A, B>().ReverseMap();
});
然后它通过 ASP.NET 启动 class.
注册
如果我在另一个地方做这样的事情 class
A instanceA; // assume A's list has values inside
var listofD = this.mapper.Map<List<D>>(A.someList)
它正确 returns 一个包含值的列表。然而:
A instanceA; // assume A's list has values inside
B instanceB = this.mapper.Map<B>(A);
returns一个B的实例,但是instanceB里面的列表是空的。我该如何解决这个问题?
您需要创建一个 custom type converter 来执行转换:
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<,>));
我已经解决了这个问题。
C# class 中的 Google.Protobuf.Collections.RepeatedField 是只读的,这意味着直接向其中赋值是行不通的,返回时只会 return 一个空列表。因此,我在两个较大的 classes 之间创建了一个自定义类型转换器以将它们组合在一起。它所做的是直接将值添加到 RepeatedField 中,而不是填充我自己的 RepeatedField 并将值分配到 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;
}
}
从 RepeatedField 转换为列表或映射中的任何其他 IEnumerable class 没有任何问题,我不需要另一个转换器。
我想在两个 class 之间映射:
public class A {
public IEnumerable<C> someList
}
和
public class B {
public RepeatedField<D> someList
}
其中 RepeatedField 是 Google.Protobuf.Collections 中处理 gRPC 数据的 class。
编辑:事实证明,gRPC 通过其原型创建 classes 的方式并不完全像创建像 B 这样的 class。请参阅我的回答。
我像这样创建一个 Automapper MappingConfiguration
return new MapperConfiguration(cfg =>
{
cfg.CreateMap<C, D>().ReverseMap();
cfg.CreateMap<A, B>().ReverseMap();
});
然后它通过 ASP.NET 启动 class.
注册如果我在另一个地方做这样的事情 class
A instanceA; // assume A's list has values inside
var listofD = this.mapper.Map<List<D>>(A.someList)
它正确 returns 一个包含值的列表。然而:
A instanceA; // assume A's list has values inside
B instanceB = this.mapper.Map<B>(A);
returns一个B的实例,但是instanceB里面的列表是空的。我该如何解决这个问题?
您需要创建一个 custom type converter 来执行转换:
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<,>));
我已经解决了这个问题。
C# class 中的 Google.Protobuf.Collections.RepeatedField 是只读的,这意味着直接向其中赋值是行不通的,返回时只会 return 一个空列表。因此,我在两个较大的 classes 之间创建了一个自定义类型转换器以将它们组合在一起。它所做的是直接将值添加到 RepeatedField 中,而不是填充我自己的 RepeatedField 并将值分配到 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;
}
}
从 RepeatedField 转换为列表或映射中的任何其他 IEnumerable class 没有任何问题,我不需要另一个转换器。