6

スタブテクニック」を使用してPOCOを更新しています(分離されたコンテキスト、ASP.NET MVCで使用されます)。

これは私が現在私のコントローラーに持っているコードです(これは機能します):

[HttpPost]
public ActionResult Edit(Review review)
{
   Review originalReview = _userContentService.FindById(review.PostId) as Review;
   var ctx = _unitOfWork as MySqlServerObjectContext;
   ctx.ApplyCurrentValues("MyEntities.Posts", review);
   _unitOfWork.Commit();
   // ..snip - MVC stuff..
}

ご覧のとおり、いたるところにコードの臭いがあります。:)

いくつかのポイント:

  1. 基本的にすべてに依存性注入(インターフェースベース)を使用します
  2. Unit of Workパターンを使用して、ObjectContextを抽象化し、複数のリポジトリ間で永続性を提供します
  3. 現在、私のIUnitOfWorkインターフェイスには1つのメソッドしかありません。void Commit();
  4. コントローラーはDIによって注入しIUserContentServiceますIUnitOfWork
  5. IUserContentServiceFindを使用するリポジトリでの呼び出しObjectContext

これらは、上記のコードでは気に入らない2つのことです。

  1. IUnitOfWorkをとしてキャストしたくありませんMySqlServerObjectContext
  2. コントローラーに気を配らせたくないApplyCurrentValues

基本的に、コードは次のようになります。

[HttpPost]
public ActionResult Edit(Review review)
{
   _userContentService.Update(review);
   _unitOfWork.Commit();
   // ..snip - MVC stuff..
}

どのように私はそれを行うことができますか?(または同様のもの)。

タイプ(ジェネリックスの組み合わせ、複数化)に基づいてエンティティセット名を計算するための賢い方法はすでにあるので、それほど心配する必要はありません。

しかし、私はどこに置くのApplyCurrentValues最適なのか疑問に思っていますか?IUnitOfWorkこれは永続性(EF)の問題であるため、インターフェイスに配置することは適切ではないようです。同じ理由で、それはサービスに属していません。これをクラスに入れるとMySqlServerObjectContext(意味があります)、このクラスに直接アクセスできるものはないため、どこから呼び出すのでしょうか。何かが要求すると、DIを介して注入されますIUnitOfWork

何かご意見は?

編集

以下にスタブ手法を使用した解決策がありますが、問題は、更新しているエンティティを事前に取得した場合、そのキーを持つエンティティがすでに存在することを示す例外をスローすることです。

どうすればこれを解決できるかわかりませんが、どちらが理にかなっていますか?

「エンティティがすでにアタッチされているかどうかを確認し、アタッチされていない場合はアタッチする」必要がありますか?

EF4の専門家は助けてくれますか?

編集

気にしないでください-解決策を見つけました。以下の回答を参照してください。

4

1 に答える 1

8

それを理解しました-簡単ではなかったので、私はできる限り説明しようとします。(気になる方へ)

コントローラ関連コード:

// _userContentService is IUserContentService
_userContentService.Update(review);

Updateしたがって、私のコントローラーは、で呼び出されたメソッドを呼び出しIUserContentService、強く型付けされたオブジェクトを通過しReviewます。

ユーザーコンテンツサービス関連コード

public void Update(Post post)
{
   // _userContentRepository is IPostRepository
   _userContentRepository.UpdateModel(post);
}

UpdateModelしたがって、私のサービスは、で呼び出されたメソッドを呼び出しIPostRepository、強く型付けされたオブジェクトを通過しReviewます。

さて、ここにトリッキーな部分があります。

私は実際に特定のリポジトリを持っていません。私は、と呼ばれる汎用リポジトリを持っています。これは、すべての異なるリポジトリGenericRepository<T> : IRepository<T>を処理します。

したがって、何かがIPostRepository(私のサービスが行っていた)を要求すると、DIはそれにを与えますGenericRepository<Post>

しかし今、私はそれに与えますPostRepository

public class PostRepository : GenericRepository<Post>, IPostRepository
{
   public void UpdateModel(Post post)
   {
      var originalPost = CurrentEntitySet.SingleOrDefault(p => p.PostId == post.PostId);
      Context.ApplyCurrentValues(GetEntityName<Post>(), post);
   }
}

また、クラスはGenericRepositoryから派生しているため、すべてのコアリポジトリロジック(検索、追加など)を継承します。

最初は、そのUpdateModelコードをGenericRepositoryクラス自体に入れようとしましたが(その後、この特定のリポジトリは必要ありませんでした)、問題は、既存のエンティティを取得するロジックが特定のエンティティキーに基づいていることです。GenericRepository<T>知らないだろう。

しかし、最終的には、ステッチがデータレイヤーの奥深くに隠され、本当にクリーンなコントローラーになります。

編集

この「スタブテクニック」も機能します。

public void UpdateModel(Post post)
{
   var stub = new Review {PostId = post.PostId};
   CurrentEntitySet.Attach(stub);
   Context.ApplyCurrentValues(GetEntityName<Post>(), post);
}

ただし、問題はPostが抽象的であるため、インスタンス化できないため、Postのタイプを確認し、派生タイプごとにスタブを作成する必要があるためです。実際にはオプションではありません。

編集2(前回)

さて、抽象クラスで動作する「スタブ手法」を取得したので、並行性の問題は解決されました。

UpdateModelメソッドにジェネリック型パラメーターと特別なnew()制約を追加しました。

実装:

public void UpdateModel<T>(T post) where T : Post, new()
{
   var stub = new T { PostId = post.PostId };
   CurrentEntitySet.Attach(stub);
   Context.ApplyCurrentValues(GetEntityName<Post>, post);
}

インターフェース:

void UpdateModel<T>(T post) where T : Post, new();

これにより、Tのタイプを手動で把握する必要がなくなり、同時実行の問題が防止され、DBへ​​の余分な移動も防止されます。

かなりグルーヴィー。

編集3(前回が前回だと思った)

上記の「スタブテクニック」は機能しますが、事前にオブジェクトを取得すると、そのキーを持つエンティティがOSMにすでに存在することを示す例外がスローされます。

誰かがこれを処理する方法をアドバイスできますか?

編集4(OK-これで終わりです!)

このSOの答えのおかげで、解決策を見つけました。EntityFrameworkのデータコンテキストにオブジェクトが既にアタッチされているかどうかを確認することはできますか?

次のコードを使用して、「エンティティがアタッチされているかどうかを確認」しようとしました。

ObjectStateEntry entry;
CurrentContext.ObjectStateManager.TryGetObjectStateEntry(entity, out entry);

しかし、OSMを調べたときでも、同じキーでエンティティを確認できたとしても、常にnullが返されました。

しかし、このコードは機能します:

CurrentContext.ObjectStateManager.TryGetObjectStateEntry(CurrentContext.CreateEntityKey(CurrentContext.GetEntityName<T>(), entity), out entry)

おそらく私がPurePOCOを使用しているため、OSMはエンティティキーを理解するのに苦労しました。

ああ、私が追加したもう1つのこと-エンティティごとに特定のリポジトリを追加する必要がないように、「[EntityKey]」(パブリックプロパティ属性)という属性を作成しました。

すべてのPOCOには、その属性で装飾された1つのパブリックプロパティが必要です。そうでない場合、リポジトリモジュールで例外をスローします。

したがって、私の汎用リポジトリは、スタブを作成/セットアップするためにこのプロパティを探します。

はい-リフレクションを使用しますが、巧妙なリフレクション(属性ベース)であり、Tからのエンティティセット名の複数化にすでにリフレクションを使用しています。

とにかく、問題は解決しました-すべて正常に動作しています!

于 2010-11-19T02:31:38.627 に答える