web-dev-qa-db-ja.com

エンティティフレームワークのビジネスロジックのベストプラクティス

初めてEntityフレームワークを使用していますが、ベストプラクティスで使用しているかどうかを知りたいです。

エンティティコンテキストを処理する別のクラスをビジネスロジックに作成しました。私が抱えている問題は、私が見たすべてのビデオで、通常、コンテキストをusingステートメントでラップして確実に閉じることですが、実際にできる前にコンテキストが閉じられるため、ビジネスロジックでこれを行うことはできませんこれを使って?

それで、私は何をしているのですか?いくつかの例:

    public IEnumerable<Article> GetLatestArticles(bool Authorised) 
    {
        var ctx = new ArticleNetEntities();
        return ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
    }

    public IEnumerable<Article> GetArticlesByMember(int MemberId, bool Authorised)
    {
        var ctx = new ArticleNetEntities();
        return ctx.Articles.Where(x => x.MemberID == MemberId && x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
    }

多くの人が使ったときに死ぬようなものを作らないようにしたいだけなの?

31
leen3o

それは本当にあなたのリポジトリ/データストアを公開したい方法に依存します。

「コンテキストが閉じられるため、ビジネスロジックを実行できない」という意味がわかりません。ビジネスロジックを使用してinsideusingステートメントを実行します。または、ビジネスロジックが別のクラスにある場合は、続けましょう。 :)

リポジトリから具体的なコレクションを返す人もいますが、その場合は、usingステートメントでコンテキストをラップできます。

public class ArticleRepository
{
   public List<Article> GetArticles()
   {
      List<Article> articles = null;

      using (var db = new ArticleNetEntities())
      {
         articles = db.Articles.Where(something).Take(some).ToList();
      }
   }
}

その利点は、接続を使用した優れたプラクティスを満足させることです。できるだけ早く開き、できるだけ早く閉じます。

Usingステートメント内にすべてのビジネスロジックをカプセル化できます。

不利な点-リポジトリは、私が個人的に好まないビジネスロジックを認識し、特定のシナリオごとに異なる方法を使用することになります。

2番目のオプション-リポジトリの一部としてコンテキストを新規作成し、IDisposableを実装します。

public class ArticleRepository : IDisposable
{
   ArticleNetEntities db;

   public ArticleRepository()
   {
      db = new ArticleNetEntities();
   }

   public List<Article> GetArticles()
   {
      List<Article> articles = null;
      db.Articles.Where(something).Take(some).ToList();
   }

   public void Dispose()
   {
      db.Dispose();
   }

}

その後:

using (var repository = new ArticleRepository())
{
   var articles = repository.GetArticles();
}

または番目のオプション(私のお気に入り)、依存性注入を使用します。すべてのコンテキスト作業をリポジトリから切り離し、DIコンテナにリソースの破棄を処理させます。

public class ArticleRepository
{
   private IObjectContext _ctx;

   public ArticleRepository(IObjectContext ctx)
   {
      _ctx = ctx;
   }

   public IQueryable<Article> Find()
   {
      return _ctx.Articles;
   }
}

選択したDIコンテナーは、具体的なObjectContextを構成された有効期間(シングルトン、HttpContext、ThreadLocalなど)でリポジトリのインスタンス化に挿入し、その構成に基づいて破棄します。

各HTTPリクエストに新しいコンテキストが与えられるように設定しています。リクエストが完了すると、DIコンテナは自動的にコンテキストを破棄します。

また、ここでは作業ユニットパターンを使用して、複数のリポジトリが1つのオブジェクトコンテキストで動作できるようにしています。

また、(具体的なリストではなく)私のリポジトリからIQueryableを返すほうがよいことに気付いたかもしれません。はるかに強力です(意味を理解していなければ、リスクは高くなります)。サービスレイヤーはIQueryableでビジネスロジックを実行し、具象コレクションをUIに返します。

これは、リポジトリのような単純なものを許可し、作業単位がコンテキストを管理し、サービスレイヤーがビジネスロジックを管理し、DIコンテナーがリソース/オブジェクトの有効期間/破棄を処理するため、これは私の最も強力なオプションです。

それについてもっと情報が必要な場合はお知らせください-驚くほど長い回答よりもさらに多くの情報があります。 :)

64
RPM1984

各クラス内でプライベート変数としてctxを使用し、毎回これの新しいインスタンスを作成して、終了時に破棄します。

public class ArticleService
{
    private ArticleEntities _ctx;

    public ArticleService()
    {
        _ctx = new ArticleEntities();
    }

    public IEnumerable<Article> GetLatestArticles(bool Authorised) 
    {            
        return _ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
    }

    public IEnumerable<Article> GetArticlesByMember(int MemberId, bool Authorised)
    {           
        return _ctx.Articles.Where(x => x.MemberID == MemberId && x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
    }

    public void Dispose()
    {
        _ctx.Dispose();
        _ctx = null;
    }

}

次に、これを呼び出すとき。

ArticleService articleService = new ArticleService();
IEnumerable<Article> article = articleService.GetLatestArticles(true);
articleService.Dispose(); // killing the connection

このようにして、同じコンテキスト内で他のオブジェクトを追加/更新し、エンティティを介してデータベースへの変更を保存するsaveメソッドを呼び出すこともできます。

3
Tim B James

私の経験では、このコードは適切ではありません。ナビゲーションプロパティを介して関係をナビゲートする機能を失うためです。

public List<Articles>  getArticles( ){  
    using (var db = new ArticleNetEntities())
    {
        articles = db.Articles.Where(something).ToList();
    }
}

このアプローチを使用すると、a.Membersは常にnullであるため、次のコードを使用できません(dbコンテキストは閉じており、データを自動的に取得できません)。

var articles = Data.getArticles();
   foreach( var a in articles ) {
       if( a.Members.any(p=>p.Name=="miki") ) {
           ...
       }
       else {
           ...
       }
    }
}

変更の削除機能を使用する必要があるため、グローバルDBコンテキストのみを使用することはお勧めできません。

アプリケーションのある時点でこれを行いますが、変更を保存せずにウィンドウを閉じます

var article= globalcontext.getArticleByID(10);
article.Approved=true;

次に、アプリケーションの別のポイントで、何らかの操作を行って保存します

//..... something
globalcontext.saveChanges();

この場合、以前の記事で承認されたプロパティは、エンティティフレームワークによって変更されるように設定されています。保存すると、承認がtrueに設定されます!!!

私にとって最善のアプローチは、クラスごとに1つのコンテキストを使用することです。必要に応じて、別の外部メソッドにコンテキストを渡すことができます。

class EditArticle {

    private DbEntities de;
    private currentAricle;

    public EditArticle() {
        de = new DbEntities; //inizialize on new istance
    }

    loadArticleToEdit(Articele a){
        // a is from another context 
        currentArticle= de.Article.Single(p=>p.IdArticle==a.IdArticle){
    }

    private saveChanges(){
        ...
        pe.saveChanges();
    }
}
3
Xilmiki

また、コンテキストをより高いレベルで保存することもできます。

たとえば、現在のコンテキストを格納する静的クラスを持つことができます:

class ContextManager
{
    [ThreadStatic]
    public static ArticleEntities CurrentContext;
}

次に、あなたの外のどこかで次のようなことをします:

using (ContextManager.CurrentContext = new ArticleEntities())
{
    IEnumerable<Article> article = articleService.GetLatestArticles(true);
}

次に、GetLastestArticles内で、同じContextManager.CurrentContextを使用します。

もちろん、これは基本的な考え方にすぎません。これは、サービスプロバイダーやIoCなどを使用することで、より機能的にすることができます。

0

Entity Frameworkの準備は、必要なすべてのEntity Framework関数用の汎用リポジトリクラスを作成することにより、データアクセスレイヤーから開始できます。次に、ビジネス層で使用できます(カプセル化)

データ、ビジネス、UIレイヤーのEntity Frameworkで使用したベストプラクティスは次のとおりです

このプラクティスで使用される手法:

  1. 適用 SOLIDアーキテクチャの原則
  2. リポジトリ設計パターンの使用
  3. あと1つのクラスのみです(準備ができていることがわかります)
0
Mina Matta