web-dev-qa-db-ja.com

Dapperを使用したリポジトリデザインパターン

これは、スタックオーバーフローではなく、コードレビューの問題かもしれません。

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

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

customerServiceとCarServiceという2つのサービスがあるとします。

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

各リポジトリのすべてのメソッドを定義し、具体的な実装を定義するインターフェイスがあります。

以下にメソッドの例を示します(ストアドプロシージャを呼び出してDB INSERTを実行します(ストアドプロシージャの実際の文字列変数は、クラスの先頭にプライベート文字列として定義されています)。

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

これはすべて正常に動作しますが、私はすべてのリポジトリのすべてのメソッドでusingブロックを繰り返していることに気付きました。以下に概説する2つの本当の質問があります。

他のすべてのリポジトリが継承し、DB接続のインスタンス化を実装するBaseRepositoryクラスを使用しておそらく何らかの方法で使用できるより良いアプローチはありますか?

システム上の複数の同時ユーザーに対してはそれでも大丈夫でしょうか?

****更新****

Silasの回答に基づいて、私は次のものを作成しました

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);
            }
        }
}

ただし、リポジトリには、次のような他の方法があります。

    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を使用してこれらをベースリポジトリに追加する正しい方法は何ですか?これにより、任意のタイプのDTOまたは任意のC#ネイティブタイプを返すことができます

17
Ctrl_Alt_Defeat

確かに、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));
}
18
Silas Reinagel

これはあなたの質問に直接関係ありません。しかし、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つのクラスは問題なく組み合わせることができます。私は個人的にそれらを分離しておくことを好みます。

また、追加のメソッドExistsDeleteById、およびDeleteAllが実装されており、これらはDapperExtensionsProxyの一部ではありません。

方法 poco.SetDbIdは各Identifierプロパティを設定するために各POCOクラスで定義されます。私の場合、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;
    }
}

上記で使用されているBasePocoは次のとおりです。

public abstract class BasePoco
{
    Guid pocoId = Guid.NewGuid();

    public Guid PocoId { get { return pocoId; } }

    public virtual void SetDbId(object id)
    {//Each POCO should override this method for specific implementation.
        throw new NotImplementedException("This method is not implemented by Poco.");
    }

    public override string ToString()
    {
        return PocoId + Environment.NewLine + base.ToString();
    }
}

また、これは here で説明されているUnitOfWorkを使用します。

8
Amit Joshi