2017-03-22 17 views
5

これは、スタックオーバーフローではなくコードレビューのための多分質問です。Dapperを使用したリポジトリデザインパターン

私はMicroORMのためにDapperを使用してSQL Server 2014にデータを取得して保存しています。DBから取得したデータを表すDTO ProjにはDTOクラスがあります。

リポジトリが必要な場合は、自分のサービスレイヤでリポジトリパターンを使用しています。その依存関係を挿入するためにコンストラクタDIを使用していて、その作業を行うリポジトリのメソッドを呼び出しています。

私は、CustomerServiceとCarServiceという2つのサービスがあるとしましょう。

次に、2つのリポジトリにCustomerRepositoryとCarRepositoryがあります。

私は各リポジトリ内のすべてのメソッドを定義し、次に具体的な実装を定義するインタフェースを持っています。

例示的な方法は、(DB INSERT(ストアドプロシージャのために実際の文字列変数に注意してくださいを実行するストアドプロシージャを呼び出すクラスの上部にプライベート文字列として定義される)を以下に示す:

public void SaveCustomer(CustomerDTO custDTO) 
    { 
     using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
     { 
      db.Execute(saveCustSp, custDTO, commandType: CommandType.StoredProcedure); 
     } 
    } 

これはすべて正常に動作しますが、すべてのリポジトリのすべてのメソッドでusingブロックを繰り返すことになります。他のすべてのリポジトリは継承し、ベースはDB接続のインスタンス化を実装します?

これは、システム上の複数の同時ユーザーでも問題なく動作しますか?

:私は、次の

public interface IBaseRepository 
{ 
    void Execute(Action<IDbConnection> query); 
} 

public class BaseRepository: IBaseRepository 
{ 
     public void Execute(Action<IDbConnection> query) 
     { 
      using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
      { 
       query.Invoke(db); 
      } 
     } 
} 

を作成したシラスの回答に基づいて

**** **** UPDATE

はしかし、私のリポジトリに、私は、以下のような他の方法を持っています

public bool IsOnlyCarInStock(int carId, int year) 
    { 
     using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
     { 
      var car = db.ExecuteScalar<int>(anotherStoredSp, new { CarID = carId, Year = year }, 
           commandType: CommandType.StoredProcedure); 

      return car > 0 ? true : false; 
     } 
    } 

public IEnumerable<EmployeeDTO> GetEmployeeDetails(int employeeId) 
    { 
     using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
     { 
      return db.Query<EmployeeDTO>(anotherSp, new { EmployeeID = employeeId }, 
           commandType: CommandType.StoredProcedure); 
     } 
    } 

Generic Type Tを使用してこれらをBaseリポジトリに追加する正しい方法は何ですか?すべてのタイプのDTOまたは任意のC#ネイティブタイプを返すことができます

+0

これは達成するための方法です。あなたのIDbConnectionを廃棄するために、あなたのBaseRepository Disposableを作成する必要があります。マイクロソフトのドキュメントでは、リポジトリパターンと作業単位のパターンを使用して作業することができます。https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef- 5-using-mvc-4/asp-net-mvc-application-in-asp-net-mvcアプリケーションのリポジトリとユニットの実装 – OrcusZ

+0

'using'ブロックはあなたが開いているので必要な悪です閉じなければならないデータベースへの接続。だから、繰り返しが必要です。私は、リポジトリデザインのパターンの内容全体に惑わされないように勧めています。 –

+0

@Callum - あなたはどのようなパターンを提案するか、例を挙げて説明できますか?私はCQRSを使って調べましたが、上記のようなリポジトリがKISS –

答えて

7

もちろん、Connectionを作成して処分する機能は素晴らしいです。

protected void Execute(Action<IDbConnection> query) 
{ 
    using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
    { 
     query.Invoke(db); 
    } 
} 

そして、あなたの簡略化呼び出しサイト:戻り値で

public void SaveCustomer(CustomerDTO custDTO) 
{ 
    Execute(db => db.Execute(saveCustSp, custDTO, CommandType.StoredProcedure)); 
} 

:あなたの呼び出しサイトで

public T Get<T>(Func<IDbConnection, T> query) 
{ 
    using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["myDB"].ConnectionString)) 
    { 
     return query.Invoke(db); 
    } 
} 

、ちょうどあなたが使用したいロジックを記述します。

public IEnumerable<EmployeeDTO> GetEmployeeDetails(int employeeId) 
{ 
    return Get<IEnumerable<EmployeeDTO>(db => 
     db.Query<EmployeeDTO>(anotherSp, new { EmployeeID = employeeId }, CommandType.StoredProcedure)); 
} 
+0

Thanks Silasに基づいて私のために働いたと感じました。病気はそれを見ている。 ExecuteメソッドをBaseリポジトリにputtinhし、それ以外のリポジトリを継承することをお勧めしますか?また、Dapper db.functionの任意のタイプのquery.Invokeが動作しますか? –

+1

この機能が必要なクラスがたくさんあると予想される場合は、ベースリポジトリを作成することをお勧めします。 –

+1

クエリ/アクションを呼び出すと、DapperメソッドとDapper以外のメソッドの両方を含む、IDbConnectionで行うことができます。 –

3

これはあなたの質問に直接関係しません。しかし、私はDapperExtensionsの使用を検討することをお勧めします。

最初は、Dapperを使用してリポジトリパターンを実装しました。欠点は、私はすべての質問を書かなければならないということでした。それは非常にひどかった。ハードコーディングされたクエリのために、汎用リポジトリを書くことは不可能に近かった。

最近、DapperExtensionsを使用するようにコードをアップグレードしました。これにより多くの問題が修正されます。あなたは上記のコードで見ることができるように

public abstract class BaseRepository<T> where T : BasePoco 
{ 
    internal BaseRepository(IUnitOfWork unitOfWork) 
    { 
     dapperExtensionsProxy = new DapperExtensionsProxy(unitOfWork); 
    } 

    DapperExtensionsProxy dapperExtensionsProxy = null; 

    protected bool Exists() 
    { 
     return (GetCount() == 0) ? false : true; 
    } 

    protected int GetCount() 
    { 
     var result = dapperExtensionsProxy.Count<T>(null); 
     return result; 
    } 

    protected T GetById(Guid id) 
    { 
     var result = dapperExtensionsProxy.Get<T>(id); 
     return result; 
    } 
    protected T GetById(string id) 
    { 
     var result = dapperExtensionsProxy.Get<T>(id); 
     return result; 
    } 

    protected List<T> GetList() 
    { 
     var result = dapperExtensionsProxy.GetList<T>(null); 
     return result.ToList(); 
    } 

    protected void Insert(T poco) 
    { 
     var result = dapperExtensionsProxy.Insert(poco); 
    } 

    protected void Update(T poco) 
    { 
     var result = dapperExtensionsProxy.Update(poco); 
    } 

    protected void Delete(T poco) 
    { 
     var result = dapperExtensionsProxy.Delete(poco); 
    } 

    protected void DeleteById(Guid id) 
    { 
     T poco = (T)Activator.CreateInstance(typeof(T)); 
     poco.SetDbId(id); 
     var result = dapperExtensionsProxy.Delete(poco); 
    } 
    protected void DeleteById(string id) 
    { 
     T poco = (T)Activator.CreateInstance(typeof(T)); 
     poco.SetDbId(id); 
     var result = dapperExtensionsProxy.Delete(poco); 
    } 

    protected void DeleteAll() 
    { 
     var predicateGroup = new PredicateGroup { Operator = GroupOperator.And, Predicates = new List<IPredicate>() }; 
     var result = dapperExtensionsProxy.Delete<T>(predicateGroup);//Send empty predicateGroup to delete all records. 
    } 

は、メソッドのほとんどはちょうどDapperExtensionsProxyクラスの基礎となるのラッパーされています

後は、一般的なリポジトリです。 DapperExtensionsProxyは内部的にUnitOfWorkも管理しています。 これらの2つのクラスは問題なく組み合わせることができます。私は個人的にそれらを別々に保つことを好みます。

Exists,DeleteById、およびDeleteAllのメソッドは、DapperExtensionsProxyの一部ではないことに気づくことがあります。

メソッドpoco.SetDbIdは、それぞれのPOCOクラスで定義され、Identifierプロパティを設定します。私の場合、POCOの識別子は異なるデータ型と名前を持つかもしれません。続き

DapperExtensionsProxyです:

internal sealed class DapperExtensionsProxy 
{ 
    internal DapperExtensionsProxy(IUnitOfWork unitOfWork) 
    { 
     this.unitOfWork = unitOfWork; 
    } 

    IUnitOfWork unitOfWork = null; 

    internal int Count<T>(object predicate) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Count<T>(predicate, unitOfWork.Transaction); 
     return result; 
    } 

    internal T Get<T>(object id) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Get<T>(id, unitOfWork.Transaction); 
     return result; 
    } 

    internal IEnumerable<T> GetList<T>(object predicate, IList<ISort> sort = null, bool buffered = false) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.GetList<T>(predicate, sort, unitOfWork.Transaction, null, buffered); 
     return result; 
    } 

    internal IEnumerable<T> GetPage<T>(object predicate, int page, int resultsPerPage, IList<ISort> sort = null, bool buffered = false) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.GetPage<T>(predicate, sort, page, resultsPerPage, unitOfWork.Transaction, null, buffered); 
     return result; 
    } 

    internal dynamic Insert<T>(T poco) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Insert<T>(poco, unitOfWork.Transaction); 
     return result; 
    } 

    internal void Insert<T>(IEnumerable<T> listPoco) where T : BasePoco 
    { 
     unitOfWork.Connection.Insert<T>(listPoco, unitOfWork.Transaction); 
    } 

    internal bool Update<T>(T poco) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Update<T>(poco, unitOfWork.Transaction); 
     return result; 
    } 

    internal bool Delete<T>(T poco) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Delete<T>(poco, unitOfWork.Transaction); 
     return result; 
    } 

    internal bool Delete<T>(object predicate) where T : BasePoco 
    { 
     var result = unitOfWork.Connection.Delete<T>(predicate, unitOfWork.Transaction); 
     return result; 
    } 
} 

これもhereを説明したUnitOfWorkを使用しています。

+0

Nice @Amit Joshi ...複数のデータベース接続文字列があれば動作しますか?はい、plsは最低限の変更を行う必要があると提案していますか?pls suggest – Ljt

+1

@LajithKumar:複数のデータベースインスタンスでこの作業を行うために変更する必要はありません。 UnitOfWorkは、この 'BaseRepository(IUnitOfWork unitOfWork)'のようにリポジトリに注入されることに注意してください。各データベースインスタンスに対して新しいUnitOfWorkを作成し、それを変更せずに同じクラス(リポジトリの新しいインスタンス)に挿入することができます。 –

関連する問題