3

私は現在、Entity FrameworkCodeFirstアプローチを使用して多くのアプリケーションを開発しました。すべての中で、私はデータリポジトリパターンを使用しています。このデータリポジトリパターンは、一度に1つのエンティティに対してクエリを実行します。たとえば、

従業員と部門の2つのモデルがあります

したがって、すべての従業員と部門を取得するには、2つのデータリポジトリインスタンスを作成します。例えば

var empRepository = new DataRepository<Employee>();
var allEmployees = empRepository.GetAll();

var depRepository = new DataRepository<Department>();
var alldepartment = depRepository.GetAll();

現在、このパターンはほとんどの場合にうまく機能します。さて、参加したいときは、このパターンではできません。両方のエンティティのすべてのレコードをフェッチした後でのみ結合を実行でき、メモリ内データで結合を使用できます。これにより、ロジックに2つのクエリの余分なオーバーヘッドが発生します。DataRepositoryパターンで使用できる優れたパターンまたはソリューションを持っている人はいますか?このパターンの代替案を提案してください。

4

4 に答える 4

3

昨日、汎用リポジトリに Join 関数を実装しました。誰もが思っているよりもはるかに簡単に実行でき、実行中に Entity Framework のいくつかの優れた機能を使用できます。

まず、このブログ投稿で書いたものと同様のリポジトリを使用しています。多くのメソッドが を返していることに気付くでしょうIQueryable。これは偶然ではありません。IQueryable遅延実行の使用を引き続き許可します。つまり、クエリは、何かが強制されるまでデータベースで実行されません (つまり、ループまたは.ToList()呼び出し)。Joinこのタイプのリポジトリに を配置すると、結合を行うためにすべてのエンティティをメモリにロードする必要がないことがわかりますIQueryable

そうは言っても、 Joinメソッドのクエリ可能なバージョンに基づいて、リポジトリに Join を取得する方法は次のとおりです。

public IQueryable<TResult> Join<TInner, TKey, TResult>(IRepository<TInner> innerRepository, Expression<Func<T, TKey>> outerSelector, Expression<Func<TInner, TKey>> innerSelector, Expression<Func<T, TInner, TResult>> resultSelector) where TInner : class
{
  return DbSet.Join(innerRepository.All(), outerSelector, innerSelector, resultSelector);
}

これにより、要求しているすべての情報を取得するために、データベースに対して 1 つのクエリのみが作成されます (これもIQueryableAll()メソッドで のみを渡すためです)。

于 2013-03-20T20:05:56.777 に答える
2

パターンを使用するとRepository、通常のシナリオのように 2 つのテーブルを結合できます。次のようにリポジトリを定義したとします。

 public interface IRepository<T>
    {
        T GetById(object id);
        void Insert(T entity);
        void Update(T entity);
        void Delete(T entity);
        IQueryable<T> Table { get; }
    }

次に、次のように 2 つのテーブルを結合できます。

from p in _someRepository.Table
join p2 in _someOtherRepository.Table on p.FOO equals p2.BAR

このアプローチでは、すべてのテーブル エントリをメモリにロードする必要はありません。

于 2013-03-20T19:37:32.893 に答える
1

まだお持ちでない場合は、次のリンクをご覧ください。

ソーシャル MSDNおよび

Forums.Asp.Net

次のように述べている@Link.frによる回答があります:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace Repository
{
  class Program
  {
    static void Main(string[] args)
    {
      // get all the informations about orders
      CustomersRepository oCustomersRepository = new CustomersRepository();
      ProductsRepository oProductsRepository = new ProductsRepository();
      OrdersRepository oOrdersRepository = new OrdersRepository();


      var query1 = oOrdersRepository.SelectAll().
        Join(oCustomersRepository.SelectAll(),
           order => order.CustomerId,
           customer => customer.Id,
           (order, customer) => new
                        {
                          MyOrder = order,
                          MyCustomer = customer
                        }).
        Join(oProductsRepository.SelectAll(),
           item => item.MyOrder.ProductId,
           product => product.Id,
           (item, product) => new 
                      { 
                        MyOrder = item.MyOrder, 
                        MyCustomer = item.MyCustomer, 
                        MyProduct = product }).
        ToList();

      foreach (var row in query1)
      {
        Console.WriteLine("query1 : {0} - {1}", row.MyCustomer.Name, row.MyProduct.Name);
      }

      Console.WriteLine("--");

または

var query2 = (from order in oOrdersRepository.SelectAll()
             join customer in oCustomersRepository.SelectAll() on order.CustomerId equals customer.Id
             join product in oProductsRepository.SelectAll() on order.ProductId equals product.Id
             select
             new
             {
               CustomerName = customer.Name,
               ProductName = product.Name
             }).ToList();

      foreach (var row in query2)
      {
        Console.WriteLine("query2 : {0} - {1}", row.CustomerName, row.ProductName);
      }



      Console.ReadKey();
    }
  }

クラスは次のようになります:-

public class Customer
  {
    public int Id { get; set; }
    public string Name { get; set; }
  }

  public class Product
  {
    public int Id { get; set; }
    public string Name { get; set; }
  }

  public class Order
  {
    public int CustomerId { get; set; }
    public int ProductId { get; set; }
  }

  public interface IRepository<T>
  {
    IList<T> SelectAll();
    IList<T> SelectAll(Func<T, bool> expression);
  }

  public class CustomersRepository : IRepository<Customer>
  {
    public IList<Customer> SelectAll()
    {
      return new List<Customer>
      {
        new Customer{ Id = 1, Name = "Customer1"},
        new Customer{ Id = 2, Name = "Customer2"},
        new Customer{ Id = 3, Name = "Customer3"},
        new Customer{ Id = 4, Name = "Customer4"}
      };
    }

    public IList<Customer> SelectAll(Func<Customer, bool> expression)
    {
      return new List<Customer>
      {
        new Customer{ Id = 1, Name = "Customer1"},
        new Customer{ Id = 2, Name = "Customer2"},
        new Customer{ Id = 3, Name = "Customer3"},
        new Customer{ Id = 4, Name = "Customer4"}
      }.Where(expression).ToList();
    }
  }

  public class ProductsRepository : IRepository<Product>
  {
    public IList<Product> SelectAll()
    {
      return new List<Product>
      {
        new Product{ Id = 1, Name = "Product1"},
        new Product{ Id = 2, Name = "Product2"},
        new Product{ Id = 3, Name = "Product3"},
        new Product{ Id = 4, Name = "Product4"}
      };
    }

    public IList<Product> SelectAll(Func<Product, bool> expression)
    {
      return new List<Product>
      {
        new Product{ Id = 1, Name = "Product1"},
        new Product{ Id = 2, Name = "Product2"},
        new Product{ Id = 3, Name = "Product3"},
        new Product{ Id = 4, Name = "Product4"}
      }.Where(expression).ToList();
    }
  }

  public class OrdersRepository : IRepository<Order>
  {
    public IList<Order> SelectAll()
    {
      return new List<Order>
      {
        new Order{ CustomerId = 1, ProductId = 1},
        new Order{ CustomerId = 1, ProductId = 2},
        new Order{ CustomerId = 2, ProductId = 3},
        new Order{ CustomerId = 3, ProductId = 4},
      };
    }

    public IList<Order> SelectAll(Func<Order, bool> expression)
    {
      return new List<Order>
      {
        new Order{ CustomerId = 1, ProductId = 1},
        new Order{ CustomerId = 1, ProductId = 2},
        new Order{ CustomerId = 2, ProductId = 3},
        new Order{ CustomerId = 3, ProductId = 4},
      }.Where(expression).ToList();
    }
  }
}

役立つかもしれません。

于 2013-03-20T19:30:30.797 に答える
1

これが、リポジトリ、特に汎用リポジトリ (エンティティ タイプごとに 1 つ) が負担になる理由の 1 つです。パターンに忠実であり続けようとすると、常にいくつかの大きな問題にぶつかります。

  • これらのぎこちない結合クエリ (linq) を書き続けます。ナビゲーション プロパティの力を実際に活用することはできません。クエリが単純すぎると、これは大きな苦痛になります。
  • したがって、ナビゲーション プロパティを使用することになるでしょう。問題は、どのリポジトリがどのエンティティを担当しているのかということです。クエリの目的は子を取得することですが、快適なエントリ ポイントは親です。
  • また、個別のリポジトリの CUD アクションを収集して「トランザクション化」するための作業単位 (UoW) も必要です。

コンテキストを直接使用しないのはなぜですか? この投稿を参照してください(引用し続けているようです)。コンテキストは、リポジトリ (DbSetまたはObjectSet) を含み、変更とトランザクション (UoW) を追跡するクラスです。このリポジトリと UoW のユニットを、リポジトリと UoW のさらに別のレイヤーでラップするのはなぜですか? それが2層です。そして、s を公開したくないため、リポジトリをクライアント コードに公開するつもりはありませんIQueryable。そのため、リポジトリを作成、調整、破棄するサービス レイヤーでラップします。それが三層。

(また、リポジトリは公開する必要IQueryableがあります。そうしないと、構成可能ではありません)。

しかし、それはテスト可能ではないと人々は言います。私たちは、単体テストのためにこれらのモック リポジトリをサービスに挿入できるように、リポジトリをモックしたいと考えています。それは錯覚です。エンティティ フレームワークの振る舞いをあざけることは欺瞞的であり、事実上不可能です。単体テストが役に立たないと言っているのではありません。オブジェクトが存在すると機能するはずのあらゆる種類のビジネス ロジックの単体テストを作成します。データ アクセス コードをテストするための単体テストは作成しません。

于 2013-03-20T21:48:13.330 に答える