我需要使用Entity Framework和AutoMapper将数据库中的整数值投影到枚举值。 问题似乎是在某些情况下列可以为空,而在其他情况下可以为非空。 如果它们是可空类型,我想使用默认值(第一个枚举值)作为空值。
这是一个完整的,最小的例子,我把我现在的方法放在一起。 它是一个控制台应用程序,安装了Entity Framework和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会选择为非可空整数指定的版本。
删除任何一个map(对于int和int?)都无济于事,同时删除它们或更改顺序。
作为旁注,我在一个更大的项目中从版本3.3.1.0迁移AutoMapper。 它似乎与该版本中定义的两个地图一起使用。
答案 0 :(得分:4)
当前的AutoMapper MapperConfiguration类中存在(IMHO)错误,该错误导致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
时,产生的效果是上述运行时异常property)使用指定的表达式(因为x
是int
,而不是int?
,因此没有HasValue
和Value
属性。
作为一种解决方法(或一般解决方案?),我建议仅定义从int
到enum
的地图,并将AutoMapper Null substitution功能用于其他部分。由于目前仅在属性映射级别支持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);
}
}
答案 1 :(得分:1)
或者你可以尝试像这样进行映射:
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));