我在使用automapper映射我的父类时遇到了一些麻烦.鉴于以下类,我创建了一个映射配置文件.
映射类:
public class SourceClass
{
public int SourceProperty1 { get; set; }
public int SourceProperty2 { get; set; }
public string SourceProperty3 { get; set; }
public string SourceProperty4 { get; set; }
}
public class TargetBaseClass
{
public int TargetProperty1 { get; set; }
public int TargetProperty2 { get; set; }
}
public class TargetClass1: TargetBaseClass
{
public string TargetProperty3 { get; set; }
}
public class TargetClass2: TargetBaseClass
{
public string TargetProperty4 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
地图:
public class MappingProfile: Profile
{
protected override void Configure()
{
CreateMap<SourceClass, TargetBaseClass>()
.Include<SourceClass, TargetClass1>()
.Include<SourceClass, TargetClass2>()
.ForMember(dst => dst.TargetProperty1, opt => opt.MapFrom(src => src.SourceProperty1))
.ForMember(dst => dst.TargetProperty2, opt => opt.MapFrom(src => src.SourceProperty2));
CreateMap<SourceClass, TargetClass1>()
.ForMember(dst => dst.TargetProperty3, opt => opt.MapFrom(src => src.SourceProperty3));
CreateMap<SourceClass, TargetClass2>()
.ForMember(dst => dst.TargetProperty4, opt => opt.MapFrom(src => src.SourceProperty4));
}
}
Run Code Online (Sandbox Code Playgroud)
最后我的计划:
static void Main(string[] args)
{
Mapper.Initialize(x => x.AddProfile<MappingProfile>());
var sourceClass = new SourceClass
{
SourceProperty1 = 1,
SourceProperty2 = 2,
SourceProperty3 = "3",
SourceProperty4 = "4"
};
var targetBaseClass = Mapper.Map<TargetBaseClass>(sourceClass);
var targetClass1 = Mapper.Map<TargetClass1>(sourceClass);
var targetClass2 = Mapper.Map<TargetClass2>(sourceClass);
Console.WriteLine("TargetBaseClass: {0} {1}", targetBaseClass.TargetProperty1,
targetBaseClass.TargetProperty2); //1 2
Console.WriteLine("TargetClass1: {0} {1} {2}", targetClass1.TargetProperty1, targetClass1.TargetProperty2,
targetClass1.TargetProperty3);//0 0 3 ???
Console.WriteLine("TargetClass2: {0} {1} {2}", targetClass2.TargetProperty1, targetClass2.TargetProperty2,
targetClass2.TargetProperty4);//1 2 4
}
Run Code Online (Sandbox Code Playgroud)
问题是,当我尝试映射派生类,我的父类的属性将不会在以下情况下映射TargetClass1,但它会为TargetClass2.任何人都可以向我解释我做错了什么,为什么这两张地图的行为不同?(我的顺序是否Include重要?)
编辑:在仔细检查,秩序不确实的事情.但是,我仍然不知道为什么只有第二个Include才算数.
Edit2:基于@GruffBunny的评论,我想我可以通过使用扩展方法"修复"这个问题.但是,我真的不明白为什么他们这样做了.看一下代码AutoMapper.TypeMap,我可以清楚地看到这个:
public void IncludeDerivedTypes(Type derivedSourceType, Type derivedDestinationType)
{
_includedDerivedTypes[derivedSourceType] = derivedDestinationType;
}
Run Code Online (Sandbox Code Playgroud)
显然,这意味着您只能为每个包含的源类型指定一个目标.但是,我没有看到任何东西,这会阻止它们支持多个目标类型.
您可以查看此自定义扩展方法.源代码也可以在Github上找到.
扩展方法有树的缺点,我现在可以想到.第一个是Mapper.AssertConfigurationIsValid()失败,因为它不会找到基本映射中定义的属性映射.解决方案是忽略基本映射中定义的任何提供的成员映射.
第二个是扩展方法取决于静态Mapper类.如果这是您使用AutoMapper的方式,那么没有问题.如果您有多个映射引擎和/或针对AutoMapper的接口编写代码,则无法使用此扩展方法.为了支持这两种情况,我们需要添加两个可选参数:IConfigurationProvider和IMappingEngine.
我尽量避免使用静态Mapper类,并通过IoC容器注入我需要它们的接口.
第三个是扩展方法不返回IMappingExpression<TSource, TDestination>和禁止覆盖基本成员映射.为了解决这个问题,我们返回 IMappingExpression<TSource, TDestination>并删除所有成员的条件.
这导致以下代码:
public enum WithBaseFor
{
Source,
Destination,
Both
}
public static class AutoMapperExtensions
{
public static IMappingExpression<TSource, TDestination> InheritMappingFromBaseType<TSource, TDestination>(
this IMappingExpression<TSource, TDestination> mappingExpression,
WithBaseFor baseFor = WithBaseFor.Both,
IMappingEngine mappingEngine = null,
IConfigurationProvider configurationProvider = null)
{
Type sourceType = typeof (TSource);
Type destinationType = typeof (TDestination);
Type sourceParentType = baseFor == WithBaseFor.Both || baseFor == WithBaseFor.Source
? sourceType.BaseType
: sourceType;
Type destinationParentType = baseFor == WithBaseFor.Both || baseFor == WithBaseFor.Destination
? destinationType.BaseType
: destinationType;
mappingExpression
.BeforeMap((sourceObject, destObject) =>
{
if (mappingEngine != null)
mappingEngine.Map(sourceObject, destObject, sourceParentType, destinationParentType);
else
Mapper.Map(sourceObject, destObject, sourceParentType, destinationParentType);
});
TypeMap baseTypeMap = configurationProvider != null
? configurationProvider.FindTypeMapFor(sourceParentType, destinationParentType)
: Mapper.FindTypeMapFor(sourceParentType, destinationParentType);
if (baseTypeMap == null)
{
throw new InvalidOperationException(
string.Format("Missing map from {0} to {1}.", new object[]
{
sourceParentType.Name,
destinationParentType.Name
}));
}
foreach (PropertyMap propertyMap in baseTypeMap.GetPropertyMaps())
mappingExpression.ForMember(propertyMap.DestinationProperty.Name, opt => opt.Ignore());
return mappingExpression;
}
}
Run Code Online (Sandbox Code Playgroud)
用法
CreateMap<SourceClass, TargetBaseClass>()
.ForMember(dst => dst.TargetProperty1, opt => opt.MapFrom(src => src.SourceProperty1))
.ForMember(dst => dst.TargetProperty2, opt => opt.MapFrom(src => src.SourceProperty2));
CreateMap<SourceClass, TargetClass1>()
.ForMember(dst => dst.TargetProperty3, opt => opt.MapFrom(src => src.SourceProperty3))
.InheritMappingFromBaseType(WithBaseFor.Destination)
;
CreateMap<SourceClass, TargetClass2>()
.ForMember(dst => dst.TargetProperty4, opt => opt.MapFrom(src => src.SourceProperty4))
.InheritMappingFromBaseType(WithBaseFor.Destination)
;
Run Code Online (Sandbox Code Playgroud)
也许还有一些场景没有被覆盖,但它确实解决了你的问题,这样你就不需要编写特定的扩展方法了.
| 归档时间: |
|
| 查看次数: |
10054 次 |
| 最近记录: |