AutoMapper - 用于可空和不可为空的int的不同ProjectUsings

时间:2016-08-17 05:48:58

标签: c# entity-framework automapper

我需要使用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。 它似乎与该版本中定义的两个地图一起使用。

2 个答案:

答案 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(在您的情况下,intMyEnumEnumValue时,产生的效果是上述运行时异常property)使用指定的表达式(因为xint,而不是int?,因此没有HasValueValue属性。

作为一种解决方法(或一般解决方案?),我建议仅定义从intenum的地图,并将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));