AutoMapper 按条件将一个 属性 映射到两个属性
AutoMapper Mapping one property to two properties by condition
有两个classes
class A
{
public string ProductionDivision { get; set; }
}
class B
{
private object _productionDivision;
public enum ProductionDivisionOneofCase
{
None = 0,
IsNullproductionDivision = 15,
ProductionDivisionValue = 16,
}
private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;
public bool IsNullProductionDivision
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
}
}
public string ProductionDivisionValue
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
}
}
}
我想根据条件将 ProductionDivision
属性 映射到 class B 的属性之一 - null
(映射到 IsNullProductionDivision
) 或不是源 属性 值的 null
(映射到 ProductionDivisionValue
)。我可以像下面这样实现它。
CreateMap<A, B>()
.ForMember(g => g.IsNullProductionDivision, m =>
{
m.PreCondition(s => s.ProductionDivision == null);
m.MapFrom(b => true);
})
.ForMember(g => g.ProductionDivisionValue, m =>
{
m.PreCondition(s => s.ProductionDivision != null);
m.MapFrom(b => b.ProductionDivision);
});
如果{source 属性 name} 的值为null
,则IsNull{source 属性 name} 的值为true
。
否则,如果 {source 属性 name} 的值不是 null
,则 {source 属性 name} 的值是 {source 属性 name} 的值。
我有很多属性响应此映射规则。所以,我不想像上面那样为每个属性编写映射规则。我想在全球范围内为这种映射配置一个规则。
如何配置 AutoMapper 才能处理如此复杂的映射?
我找到了解决办法。解决方案非常简单明了。结果如下:
完整代码:
public class Program
{
class A
{
public string ProductionDivision { get; set; }
}
class B
{
private object _productionDivision;
public enum ProductionDivisionOneofCase
{
None = 0,
IsNullproductionDivision = 15,
ProductionDivisionValue = 16,
}
private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;
public bool IsNullProductionDivision
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
}
}
public string ProductionDivisionValue
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
}
}
}
public class StrinToBoolCustomResolver
: IValueConverter<string, bool>
{
public bool Convert(string sourceMember, ResolutionContext context)
{
return sourceMember == null;
}
}
public class MyAutoMapperProfile
: Profile
{
public MyAutoMapperProfile()
{
// add post and pre prefixes to add corresponding properties in the inner property map
RecognizeDestinationPostfixes("Value");
RecognizeDestinationPrefixes("IsNull");
// add mapping for "value" property
this.ForAllPropertyMaps(map => map.SourceMember.Name + "Value" == map.DestinationName,
(map, expression) =>
{
expression.Condition((source, dest, sMem, destMem) =>
{
return sMem != null;
});
expression.MapFrom(map.SourceMember.Name);
});
// add mapping for "IsNull" property
this.ForAllPropertyMaps(map => "IsNull" + map.SourceMember.Name == map.DestinationName,
(map, expression) =>
{
expression.Condition((source, dest, sMem, destMem) =>
{
return (bool)sMem;
});
//expression.MapFrom(map.SourceMember.Name);
expression.ConvertUsing<string, bool>(new StrinToBoolCustomResolver(), map.SourceMember.Name);
});
CreateMap<A, B>();
//.ForMember(g => g.IsNullProductionDivision, m =>
//{
// m.PreCondition(s => s.ProductionDivision == null);
// m.MapFrom(b => true);
//});
//.ForMember(g => g.ProductionDivisionValue, m =>
//{
// m.PreCondition(s => s.ProductionDivision != null);
// m.MapFrom(b => b.ProductionDivision);
//});
}
}
public static void Main(string[] args)
{
var configuration = new MapperConfiguration(cfg => {
cfg.AddProfile(new MyAutoMapperProfile());
});
var mapper = new Mapper(configuration);
mapper.ConfigurationProvider.AssertConfigurationIsValid();
var a = new A()
{
ProductionDivision = null
};
// b._productionDivisionCase will be equal IsNullproductionDivision
var b = mapper.Map<B>(a);
var c = new A()
{
ProductionDivision = "dsd"
};
// d._productionDivisionCase will be equal ProductionDivisionValue
var d = mapper.Map<B>(c);
}
}
澄清:
add (post/pre)fixes 将相应的属性添加到内部 属性 映射。它需要在这里做,因为我们的属性应该被 AutoMapper 捕获。否则属性将被放弃并且映射器配置将失败。
之后,我们配置这些属性需要如何映射。我们调用 ForAllPropertyMaps
方法,过滤所有属性并设置规则以映射适合我们过滤器的属性。创建映射器对象时,将根据指定的过滤器构建执行计划。
- 对于“值”属性我们添加一个条件来检查源属性是否不为空。如果为空,则映射将丢失。
- 对于“IsNull”属性 首先我们添加一个转换器,将string类型转换为bool类型。转换器只是将源字符串 属性 与空值进行比较。如果源 属性 等于 null,则转换器 returns 为真。因此,条件接收到一个真值,returns 真,映射将完成。否则转换器 returns false,因此条件 returns false 和映射将丢失。
因此,根据源 属性 是否为空值,源 属性 将映射到不同的目标属性。此外,如果不是必须调用相应目标属性的相应设置方法,则不会调用。
有两个classes
class A
{
public string ProductionDivision { get; set; }
}
class B
{
private object _productionDivision;
public enum ProductionDivisionOneofCase
{
None = 0,
IsNullproductionDivision = 15,
ProductionDivisionValue = 16,
}
private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;
public bool IsNullProductionDivision
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
}
}
public string ProductionDivisionValue
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
}
}
}
我想根据条件将 ProductionDivision
属性 映射到 class B 的属性之一 - null
(映射到 IsNullProductionDivision
) 或不是源 属性 值的 null
(映射到 ProductionDivisionValue
)。我可以像下面这样实现它。
CreateMap<A, B>()
.ForMember(g => g.IsNullProductionDivision, m =>
{
m.PreCondition(s => s.ProductionDivision == null);
m.MapFrom(b => true);
})
.ForMember(g => g.ProductionDivisionValue, m =>
{
m.PreCondition(s => s.ProductionDivision != null);
m.MapFrom(b => b.ProductionDivision);
});
如果{source 属性 name} 的值为null
,则IsNull{source 属性 name} 的值为true
。
否则,如果 {source 属性 name} 的值不是 null
,则 {source 属性 name} 的值是 {source 属性 name} 的值。
我有很多属性响应此映射规则。所以,我不想像上面那样为每个属性编写映射规则。我想在全球范围内为这种映射配置一个规则。
如何配置 AutoMapper 才能处理如此复杂的映射?
我找到了解决办法。解决方案非常简单明了。结果如下:
完整代码:
public class Program
{
class A
{
public string ProductionDivision { get; set; }
}
class B
{
private object _productionDivision;
public enum ProductionDivisionOneofCase
{
None = 0,
IsNullproductionDivision = 15,
ProductionDivisionValue = 16,
}
private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;
public bool IsNullProductionDivision
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
}
}
public string ProductionDivisionValue
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
}
}
}
public class StrinToBoolCustomResolver
: IValueConverter<string, bool>
{
public bool Convert(string sourceMember, ResolutionContext context)
{
return sourceMember == null;
}
}
public class MyAutoMapperProfile
: Profile
{
public MyAutoMapperProfile()
{
// add post and pre prefixes to add corresponding properties in the inner property map
RecognizeDestinationPostfixes("Value");
RecognizeDestinationPrefixes("IsNull");
// add mapping for "value" property
this.ForAllPropertyMaps(map => map.SourceMember.Name + "Value" == map.DestinationName,
(map, expression) =>
{
expression.Condition((source, dest, sMem, destMem) =>
{
return sMem != null;
});
expression.MapFrom(map.SourceMember.Name);
});
// add mapping for "IsNull" property
this.ForAllPropertyMaps(map => "IsNull" + map.SourceMember.Name == map.DestinationName,
(map, expression) =>
{
expression.Condition((source, dest, sMem, destMem) =>
{
return (bool)sMem;
});
//expression.MapFrom(map.SourceMember.Name);
expression.ConvertUsing<string, bool>(new StrinToBoolCustomResolver(), map.SourceMember.Name);
});
CreateMap<A, B>();
//.ForMember(g => g.IsNullProductionDivision, m =>
//{
// m.PreCondition(s => s.ProductionDivision == null);
// m.MapFrom(b => true);
//});
//.ForMember(g => g.ProductionDivisionValue, m =>
//{
// m.PreCondition(s => s.ProductionDivision != null);
// m.MapFrom(b => b.ProductionDivision);
//});
}
}
public static void Main(string[] args)
{
var configuration = new MapperConfiguration(cfg => {
cfg.AddProfile(new MyAutoMapperProfile());
});
var mapper = new Mapper(configuration);
mapper.ConfigurationProvider.AssertConfigurationIsValid();
var a = new A()
{
ProductionDivision = null
};
// b._productionDivisionCase will be equal IsNullproductionDivision
var b = mapper.Map<B>(a);
var c = new A()
{
ProductionDivision = "dsd"
};
// d._productionDivisionCase will be equal ProductionDivisionValue
var d = mapper.Map<B>(c);
}
}
澄清:
add (post/pre)fixes 将相应的属性添加到内部 属性 映射。它需要在这里做,因为我们的属性应该被 AutoMapper 捕获。否则属性将被放弃并且映射器配置将失败。
之后,我们配置这些属性需要如何映射。我们调用
ForAllPropertyMaps
方法,过滤所有属性并设置规则以映射适合我们过滤器的属性。创建映射器对象时,将根据指定的过滤器构建执行计划。
- 对于“值”属性我们添加一个条件来检查源属性是否不为空。如果为空,则映射将丢失。
- 对于“IsNull”属性 首先我们添加一个转换器,将string类型转换为bool类型。转换器只是将源字符串 属性 与空值进行比较。如果源 属性 等于 null,则转换器 returns 为真。因此,条件接收到一个真值,returns 真,映射将完成。否则转换器 returns false,因此条件 returns false 和映射将丢失。
因此,根据源 属性 是否为空值,源 属性 将映射到不同的目标属性。此外,如果不是必须调用相应目标属性的相应设置方法,则不会调用。