1

何が背後にあるのかを理解し、それを自分で行う方法を学びたいので、 ModeltoViewModelViewModeltoModelを変換する良い方法を知りたいです。AutoMapperもちろん、モデルとは、EFによって生成されたクラスを意味します。

これまでにこのようなものを作成しましたが、ネストされたクラスが関係する場合、いくつかの問題があります。

    // to VM
    public static Author ToViewModel(EntityAuthor author)
    {
        if (author == null)
            return null;

        Author result = new Author();
        result.Id = author.ATH_ID;
        result.FirstName = author.ATH_FirstName;
        result.LastName = author.ATH_LastName;
        return result;
    }
    public static BlogPost ToViewModel(EntityBlogPost post)
    {
        if (post == null)
            return null;

        Experiment result = new Experiment();
        result.Id = post.BP_ID;
        result.Title = post.BP_Title;
        result.Url = post.BP_Url;
        result.Description = post.BP_Description;
        result.Author = ToViewModel(post.Author);
        return result;
    }


    // from VM 
    public static EntityAuthor ToModel(Author author)
    {
        if (author == null)
            return null;

        EntityAuthor result = new EntityAuthor();
        result.ATH_ID= author.Id;
        result.ATH_FirstName = author.FirstName;
        result.ATH_LastName = author.LastName;
        return result;
    }
    public static EntityBlogPost ToModel(BlogPost post)
    {
        if (post == null)
            return null;

        EntityBlogPost result = new EntityBlogPost();
        result.BP_ID = post.Id;
        result.BP_Title = post.Title;
        result.BP_Url = post.Url;
        result.BP_Description = post.Description;
        result.Author = ToModel(post.Author);
        return result;
    }

注:EntityBlogPostは、への外部キーを保持しますEntityAuthor。私が今直面している問題の1つは、BlogPostを編集する場合、対応するエンティティで作成者の外部キー「BP_ATH_ID」を設定する必要がありますが、編集した投稿の作成者が「null」であるため、これは「0」です。著者をhttp-postしたくありません。それでも、(http-get中に)表示したいので、作成者はビューモデルにいる必要があります。これが私のコントローラーです(ビューは重要ではありません):

    // GET: I make use of Author for this
    public ActionResult Edit(int id)
    {
        return View(VMConverter.ToViewModel(new BlogPostService().GetByID(id)));
    }

    //
    // POST: I don't make use of Author for this
    [HttpPost]
    public ActionResult Edit(BlogPost input)
    {
        if (ModelState.IsValid)
        {                
            new BlogPostService().Update(VMConverter.ToModel(input));
            return RedirectToAction("List");
        }
        return View(input);
    }

現時点では、コントローラーの背後にいくつかのサービスModelがあり、これらは(私のコードでわかるように)上でのみ機能します。この「サービスレイヤー」を他のアプリケーションにも再利用することが目的でした。

    public void Update(EntityBlogPost post)
    {
        // let's keep it simple for now
        this.dbContext.Entry(post).State = EntityState.Modified;
        this.dbContext.SaveChanges();
    }

では、私の質問に戻りましょう。この遷移Model->ViewModelを処理して戻るための良い方法は何でしょうか?

4

2 に答える 2

5

私の意見では、このアプローチは両方向で問題があります。

  • モデルからViewModel(GETリクエスト)

    このような方法を使用している場合...

    public static Author ToViewModel(EntityAuthor author)
    

    ...質問は:どこEntityAuthor authorから入手しますか?もちろん、FindorSingleまたは何かを使用してデータベースからロードします。これにより、EntityAuthorエンティティ全体がすべてのプロパティで具体化されます。ビューにそれらすべてが必要ですか?たぶんそうです、この簡単な例では。ただし、顧客、配送先住所、注文アイテム、担当者、請求書アドレスなど、他のエンティティへの参照が多い大規模なエンティティを想像してみてOrderください。また、期日、顧客など、一部のプロパティのみを含むビューを表示したいとします。名前、連絡先の電子メールアドレス。

    メソッドを適用するには、ビューに必要のない一連のプロパティをToViewModelロードする必要があり、関連するエンティティにも適用する必要があります。これにより、これらのエンティティのすべてのプロパティが再度読み込まれますが、ビューで必要なのはそれらの選択だけです。EntityOrderInclude

    ビューに必要なプロパティのみをロードする通常の方法は、投影です。次に例を示します。

    var dto = context.Orders.Where(o => o.Id == someOrderId)
        .Select(o => new MyViewDto
        {
            DueDate = o.DueDate,
            CustomerName = o.Customer.Name,
            ContactPersonEmailAddress = o.ContactPerson.EmailAddress
        })
        .Single();
    

    ご覧のとおり、新しいヘルパークラスを導入しましたMyViewDtoToViewModelこれで、特定のメソッドを作成できます。

    public static OrderViewModel ToMyViewModel(MyViewDto dto)
    

    dtoとviewModelの間のマッピングは、AutoMapperの適切な候補です。(上記の投影ステップにAutoMapperを使用することはできません。)

    別の方法は、ViewModelに直接投影することです。つまり、MyViewDto上記を。に置き換えますOrderViewModel。ViewModelsが存在する場所でも、ビューレイヤーに公開する必要がIQueryable<Order>あります。一部の人々はそれを気に入らないので、個人的にはこのアプローチを使用しています。

    ToMyViewModel欠点は、基本的にすべてのビューに対して別のメソッドである、タイプの多くの異なるメソッドが必要になることです。

  • ViewModelからModel(POSTリクエスト)

    これは、例ですでに気付いているように、より大きな問題です。多くのビューには、完全なエンティティが表示されないか、「ビューのみ」であると想定されてサーバーにポストバックされないエンティティデータが表示されません。

    メソッドを使用する場合(AutoMapperを使用するかどうかに関係なく)...

    public static EntityAuthor ToModel(Author author)
    

    ...EntityAuthorビューモデルによって表されるビューにAuthor authorはすべてのプロパティが表示されておらず、少なくともすべてのプロパティが返されるわけではないため、ほとんどの場合、明らかに完全なオブジェクトを作成することはありません。Update次のような方法を使用します。

    this.dbContext.Entry(post).State = EntityState.Modified;
    

    ...データベース内のエンティティを部分的に破棄します(または、必要なFKまたはプロパティが正しく設定されていないため、最良の場合は例外をスローします)。正しい更新を実現するには、データベースに保存されている値をマージする必要があり、変更された値がビューからポストバックされて変更されません。

    ビューに合わせた特定の方法を使用できます。Update

    public void UpdateForMyView1(EntityBlogPost post)
    {
        this.dbContext.EntityBlogPosts.Attach(post);
        this.dbContext.Entry(post).Property(p => p.Title).IsModified = true;
        this.dbContext.Entry(post).Property(p => p.Description).IsModified = true;
        this.dbContext.SaveChanges();
    }
    

    これは、の編集のみを許可するビューのメソッドになりTitleます。特定のプロパティをEFとしてマークすると、データベース内のそれらの列のみが更新されます。DescriptionEntityBlogPostModified

    別の方法は、DTOを再度導入し、ビューモデルとそれらのDTOの間にメソッドをマッピングすることです。

    public static MyUpdateAuthorDto ToMyUpdateAuthorDto(Author author)
    

    これは、プロパティのコピーまたはAutoMapperのみです。更新は次の方法で実行できます。

    public void UpdateForMyView1(MyUpdateAuthorDto dto)
    {
        var entityAuthor = this.dbContext.EntityAuthors.Find(dto.AuthorId);
        this.dbContext.Entry(entityAuthor).CurrentValues.SetValues(dto);
        this.dbContext.SaveChanges();
    }
    

    これにより、一致するプロパティのみが更新され、EntityAuthor変更されたかdtoのようにマークされます。Modifiedこれにより、外部キーがdtoの一部ではなく、更新されないため、外部キーが欠落しているという問題が解決されます。データベースの元の値は変更されません。

    SetValuesをパラメータとして取ることに注意しobjectてください。したがって、ある種の再利用可能なUpdateメソッドを使用できます。

    public void UpdateScalarAuthorProperties(int authorId, object dto)
    {
        var entityAuthor = this.dbContext.EntityAuthors.Find(authorId);
        this.dbContext.Entry(entityAuthor).CurrentValues.SetValues(dto);
        this.dbContext.SaveChanges();
    }
    

    このアプローチは、スカラーおよび複雑なプロパティの更新に対してのみ機能します。ビューが関連するエンティティまたはエンティティ間の関係を変更することを許可されている場合、手順はそれほど簡単ではありません。この場合、あらゆる種類の更新に対して特定のメソッドを作成する以外の方法はわかりません。

于 2012-06-15T14:49:12.560 に答える
1

これらの遷移を処理する良い方法は、AutoMapperを使用することです。それが、オートマッパーが実際に作成された目的です。

それがどのように機能するかを知りたい場合は、assemlbyデコンパイラー(ILSpyはその1つです)を使用して、AutoMapper.dllで使用してください。

ここでの魔法の言葉はReflectionです。

皮切りに:

foreach (PropertyInfo prop in typeof(EntityAuthor).GetProperties())
{
   ...
}

リフレクションメカニズムを使用すると、ソースクラスと宛先クラスのすべてのプロパティを一覧表示し、それらの名前を比較できます。これらの名前を一致さSetValueせると、ソースオブジェクトの値に基づいて、メソッドを使用して宛先オブジェクトのプロパティを設定できます。

于 2012-06-14T23:48:38.390 に答える