1

Web API プロジェクトで DTO を受け取っています。AutoMapper を使用して、DTO をデータベースに挿入するエンティティに自動的に変換したいと考えています。

DTO とエンティティを簡略化したものを次に示します。

class RegistrationDTO
{
    string name;
    ICollection<int> Departments;
}

class Registration
{
    int id;
    DateTime CreatedAt;
    string name;
    virtual ICollection<Department> Departments;
}

class Department
{
    int id;
    string name;
    virtual ICollection<Registration> Registrations;
}

問題は、RegistrationDTO には部門の ID しかなく、AutoMapper がデータベースから部門を取得する方法が見つからないことです (Entity Framework 5 を使用)。

カスタム ValueResolver を使用すると、int のリストを部門のリストに変換できますが、新しい部門を作成するのではなく、データベースから部門を取得したいと考えています。

これは私が思いついた解決策ですが、それを行うためのより良い方法があると確信しています:

var reg= Mapper.Map<Registration>(dto);

reg.Departments = new List<int>(dto.Departments).ConvertAll(input => Context.Departments.Find(input));

if(reg.Departments.Contains(null)) //a department provided does not exist in the database
    return Request.CreateResponse(HttpStatusCode.BadRequest, "invalid department");

...

誰でもこれで私を助けることができますか?

4

1 に答える 1

5

一般に、Automapper を使用して DTO データからエンティティをインフレートすることはお勧めできません。エンティティからビューモデル、webapimodels、または一般的な DTO にデータを渡すなど、反対方向に進むのに最適です。しかし、特に EntityFramework では、それをクライアントからドメインへの方向で使用すると、面倒になる可能性があります。

たとえば、ビューモデルにない 2 つのプロパティがエンティティにあるとしidますCreatedAt。が例外をスローしないようにするには、プロパティの無視またはカスタム リゾルバーに加えて、呼び出しでAutoMapper.Mapper.AssertConfigurationIsValid()これら 2 つのプロパティのカスタム リゾルバーを無視または使用する必要があることを意味します。最終的に、自動マッピングされるのは だけであり、そもそも automapper を使用する目的を無効にします。CreateMapDepartmentsname

DTO をエンティティに変換するためのコードは、実際にはかなり簡潔です。正直なところ、私が変更する主な点は automapper の削除です。この場合、それは本当に必要ありません。

var reg = new Registration { name = dto.name }; // less code than with automapper

reg.Departments = new List<int>(dto.Departments)
    .ConvertAll(input => Context.Departments.Find(input));

if(reg.Departments.Contains(null)) //a department provided does not exist in the database
    return Request.CreateResponse(HttpStatusCode.BadRequest, "invalid department");

次のようなことを試してみたくなるかもしれません:

Mapper.CreateMap<RegistrationDTO, Registration>()
   .ForMember(d => d.id, o => o.Ignore())
   .ForMember(d => d.CreatedAt, o => o.UseValue(DateTime.Now))
   .ForMember(d => d.Departments, o => o.MapFrom(s => 
   {
       var dbContext = new MyDbContext();
       var departments = new List<int>(s.Departments)
           .ConvertAll(input => dbContext.Departments.Find(input));
       return departments;
   }))
;

DbContextデリゲート ブロック内の は、エンティティを ( )DbContextに追加して呼び出すために使用するものと同じではないため、これは機能しません。異なるコンテキストに関連付けられたエンティティがある場合、データベース内でエンティティが重複することになります (または、主キーの重複による SQL 例外が発生する可能性があります)。RegistrationdbContext.Registrations.Add(reg)SaveChangesDepartment

アップデート

私のエンティティと DTO の両方に 15 以上のフィールドがあるため、AutoMapper を選択しました。この 2 つの唯一の違いは、ID、作成日、最終更新日など、私のエンティティが持つデータベース固有のものです。使用しないというアドバイスを維持しますか?この場合の AutoMapper は、私のエンティティがここに投稿した単純化よりもはるかに大きいことを考慮して?

場合によります。15 以上の他のプロパティについて、それらはすべてスカラーですか? 外部キー プロパティ (コレクション以外のナビゲーション プロパティを管理するために公開されているもの) はありますか? カスタムリゾルバーを呼び出すものはいくつありますか?

DTO コレクション ナビゲーション プロパティ ( ) に automapper を使用することは絶対にありませんpublic virtual ICollection<SomeOtherEntity> OtherEntities { get; set; }。また、外部キー ( ) を公開しない DTO 非コレクション ナビゲーション プロパティにオートマッパーを使用しようとはしませんpublic virtual SomeOtherEntity OtherEntity { get; set; }

ここでのコードの匂いは、DTO からエンティティへの呼び出しごとにCreateMap、少なくとも 2 つの が存在することですIgnore(作成日、最終更新日などは無視されます)。また、コレクション以外の nav プロパティが外部キー プロパティを公開する場合、fk プロパティを自動マップして機能しますが、実際の ( virtual) nav プロパティを別の無視することになります。

また、ドメイン コードに関して言えば、それは記録のシステムであり、AutoMapper の背後に一部の詳細を隠すのではなく、それを読むときにすべてを公開するのに役立ちます。以下を考えてみてください -- これはより明確で、多少冗長ですが、すべてのドメイン転送コードが 1 つのソース ファイルに示されているため、必ずしも悪いことではないと思います。

var reg = new Registration
{
    name = dto.name,
    prop1 = dto.prop1,
    prop2 = dto.prop2,
    ...
    propN = dto.propN
};

CreateMapブートストラッパーで必要なすべての余分な行 (ignores、カスタム リゾルバーなど) と、ここにある余分な行の数を比較してください。最終的にはあなたの電話です。うまくいけば、これが役に立ちます。

于 2013-03-23T06:57:58.187 に答える