2016-08-17 7 views
4

Entity FrameworkとAutoMapperを使用してデータベースから列挙値に整数値を投影する必要があります。 問題は、列がnullの場合があり、他の場合はnullを指定できないことが考えられます。 null型の場合は、nullにデフォルト値(最初のenum値)を使用します。AutoMapper - null可能でnullでないintの異なるプロジェクト使用

ここに私の現在のアプローチをまとめた完全な最小限の例があります。 これは、Entity FrameworkとAutoMapperの現在のナゲットパッケージがインストールされたコンソールアプリケーションです。 また、以下のようなテーブルで(最初のデータベースの)データベースを必要とする:

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はnullを許可しない整数に指定されたバージョンを選択すると仮定しました。 どちらのマップ(intとint?)を削除しても、両方のマップを削除したり、順序を変更したりすることはできません。

大きなプロジェクトでAutoMapperをバージョン3.3.1.0から移行しています。 そのバージョンで定義されている両方のマップで動作しているようです。

答えて

4

(私見)既にをオーバーライド使用するNullable<TSource> -> TDestinationに指定したマッピングを引き起こしている現在のAutoMapper MapperConfigurationクラスのバグが(あり指定されています)。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は、指定を使用して(EnumValueプロパティのMyEnumint、あなたのケースで)TDestinationTSourceをマップしようとしていると、得られる効果は、前述の実行時例外であります(xintであり、予想通りint?ではないため、HasValueおよびValueという特性はありません)。

回避策(または一般的な解決策)として、intからenumへのマップのみを定義し、他の部分にはAutoMapper Null substitution機能を使用することをお勧めします。あなたの答えをありがとうは

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

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

とこのようにそれを使用します。現在、ヌル置換は、それが簡単に私はこのようなヘルパーメソッドでそれをカプセル化するだろう作るために、唯一のプロパティ・マップ・レベルでサポートされていますので、これはうまくいくでしょうが、より一般的な解決策を好むでしょう。これは、この投影法がいくつかの場所で使われているより大きなプロジェクトから取られた単なる例です。ユニットテストでカバーされていないプロパティがいくつか見逃される可能性があり、生産時まで気づかない可能性があります。もちろん、より一般的な解決法はDRYになり、大きな利点になります。しかし、アイデアをありがとう! –

+0

非常に真です。私はもっ​​と一般的な解決策を考え出すことができるかどうかを見ていきます。 – juunas

関連する問題