2016-08-17 107 views
4

我需要從數據庫中投射整數值,以使用Entity Framework和AutoMapper枚舉值。 問題似乎是列在某些情況下可以爲空而在其他情況下爲非空。 如果它們是可爲空的類型,我想爲默認值使用默認值(第一枚舉值)。AutoMapper - 可爲空或不可空的整數的不同的ProjectUsings

這裏是一個完整的,最小的例子,我把我目前的做法放在一起。 它是一個控制檯應用程序,包含實體框架和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?)都無濟於事,也無法移除它們或更改順序。

作爲一個方面說明,我正在將AutoMapper從版本3.3.1.0遷移到更大的項目中。 它似乎與該版本中定義的兩個地圖一起工作。

回答

4

有被使用(恕我直言)在當前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試圖(int在你的情況下,爲了MyEnumEnumValue屬性)映射TSourceTDestination上述運行時異常:它可以容易地通過執行以下代碼中可以看出表達式(因爲xint,而不是int?如預期的那樣,因此沒有HasValueValue屬性)。

作爲一種變通方法(或一般的解決方案?)我建議僅定義在地圖從intenum,並使用用於另一部分的AutoMapper Null substitution特徵。由於目前空替代僅在屬性映射級別的支持,以方便我將其封裝在一個輔助方法是這樣的:

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); 
    } 
} 
+0

謝謝,這工作完美,看起來像一個堅實的解決方案!我將在AutoMapper存儲庫上創建一個GitHub問題,詢問他們是否可以確認這是一個錯誤,以及它是否會被修復。 –

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)); 
+0

謝謝您的回答 - 這是可行的,但我更願意採用更一般的解決方案,因爲這只是一個大型項目的示例,其中此項投影在多個地方使用。很可能我會錯過一些未被單元測試覆蓋的屬性,並且可能會在生產之前被忽視。當然,更通用的解決方案也會更加乾燥,這將是一個巨大的優勢。謝謝你的想法! –

+0

非常真實。我會看看我能否想出更通用的解決方案。 – juunas