AutoMapper -可空型和不可空型的不同投影
本文关键字:空型 投影 AutoMapper | 更新日期: 2023-09-27 18:02:45
我需要从数据库到使用实体框架和AutoMapper的枚举值项目整数值。问题似乎是列在某些情况下可以为空,而在其他情况下不可为空。如果它们是可空类型,我希望为空使用默认值(第一个enum值)。
这是一个完整的,最小的例子,我把我目前的方法。它是一个控制台应用程序,安装了实体框架和AutoMapper的当前nuget包。它还需要一个数据库(首先是数据库),其中包含如下表:
CREATE TABLE [dbo].[MyTable] (
[Id] INT PRIMARY KEY,
[EnumValue] INT NOT NULL,
[EnumValueNullable] INT
)
控制台应用程序的代码(c#):
public enum MyEnum
{
Value1 = 0,
Value2 = 1
}
public class MyTableModel
{
public int Id { get; set; }
public MyEnum EnumValue { get; set; }
public MyEnum EnumValueNullable { get; set; }
}
public class MyProfile : Profile
{
public MyProfile()
{
this.CreateMap<MyTable, MyTableModel>();
this.CreateMap<int, MyEnum>().ProjectUsing(x => (MyEnum)x);
this.CreateMap<int?, MyEnum>().ProjectUsing(x => x.HasValue ? (MyEnum)x.Value : MyEnum.Value1);
}
}
static void Main(string[] args)
{
var config = new MapperConfiguration(x => x.AddProfile(new MyProfile()));
var result = new MyDataEntities().MyTable.ProjectTo<MyTableModel>(config).ToList();
}
在执行这段代码时,它告诉我HasValue
不是为System.Int32
类型定义的。这当然是正确的,但是我假设AutoMapper会选择为非空整数指定的版本。删除任意一个映射(对于int和int?)都没有帮助,同时删除它们或更改顺序也没有帮助。
作为旁注,我正在一个更大的项目中从3.3.1.0版本迁移AutoMapper。该版本中定义的两个map似乎都可以使用
有(IMHO)一个错误在当前的AutoMapper MapperConfiguration类导致指定的映射为Nullable<TSource> -> TDestination
被使用(覆盖已经指定的)为TSource -> TDestination
。通过执行以下代码可以很容易地看到:
var config = new MapperConfiguration(x => x.AddProfile(new MyProfile()));
var m1 = config.ResolveTypeMap(typeof(int), typeof(MyEnum));
var m2 = config.ResolveTypeMap(typeof(int?), typeof(MyEnum));
Console.WriteLine(m1 == m2); // true
结果是上述运行时异常,当AutoMapper试图映射TSource
到TDestination
(在你的情况下,int
到MyEnum
的EnumValue
属性)使用指定的表达式(因为x
是int
,而不是int?
如预期的,因此没有HasValue
和Value
属性)。
作为变通方法(或通用解决方案?)我建议只定义从int
到enum
的映射,并对其他部分使用AutoMapper Null替换特性。由于目前只在属性映射级别支持空替换,为了使它更容易,我将它封装在一个助手方法中,如下所示:
public static class AutoMapperExtensions
{
public static void NullSubstitute<TSource, TDestination>(this Profile profile, TSource nullSubstitute)
where TSource : struct
{
object value = nullSubstitute;
profile.ForAllPropertyMaps(
map => map.SourceType == typeof(TSource?) &&
map.DestinationPropertyType == typeof(TDestination),
(map, config) => config.NullSubstitute(value)
);
}
}
,并像这样使用:
public class MyProfile : Profile
{
public MyProfile()
{
this.CreateMap<MyTable, MyTableModel>();
this.CreateMap<int, MyEnum>().ProjectUsing(x => (MyEnum)x);
this.NullSubstitute<int, MyEnum>((int)MyEnum.Value1);
}
}
或者您可以尝试这样做映射:
this.CreateMap<MyTable, MyTableModel>()
.ForMember(dest => dest.EnumValue, o => o.MapFrom(src => (MyEnum)src.EnumValue))
.ForMember(dest => dest.EnumValueNullable, o => o.MapFrom(src => src.EnumValueNullable != null ? src.EnumValueNullable.Value : MyEnum.Value1));