web-dev-qa-db-ja.com

.NET Coreを使用したDapper-挿入されたSqlConnectionライフタイム/スコープ

.NET Core Dependency Injectionを使用して、アプリケーションの起動時にSqlConnectionオブジェクトをインスタンス化し、それをリポジトリに挿入することを計画しています。このSqlConnectionは、リポジトリの実装内のデータベースからデータを読み書きするためにDapperによって使用されます。 Dapperでasync呼び出しを使用します。

問題は、SqlConnectionを一時的なものとして注入するか、シングルトンとして注入するかです。 asyncを使用したいという事実を考えると、Dapperが内部でいくつかの分離コンテナーを実装し、シングルトンのスコープがDapperが内部で使用するスコープ内にラップされない限り、一時的なものを使用すると思います。

Dapperを使用する場合のSqlConnectionオブジェクトの存続期間に関する推奨事項/ベストプラクティスはありますか?私が見逃しているかもしれない注意事項はありますか?

前もって感謝します。

13
Phil P.

SQL接続をシングルトンとして提供する場合、MARSを有効にしない限り、同時に複数の要求を処理できません。これには、MARSにも制限があります。ベストプラクティスは、一時的なSQL接続を使用して、適切に破棄されるようにすることです。

私のアプリケーションでは、IDbConnectionFactoryステートメント内で接続を作成するために使用されるリポジトリにカスタムusingを渡します。この場合、ヒープ自体の割り当てを減らすために、リポジトリ自体をシングルトンにすることができます。

6
Andrii Litvinov

回答もコメントも、@ Andrii Litvinovに同意します。

この場合、データソース固有の接続ファクトリのアプローチを使用します。

同じアプローチで、私は別の方法-UnitOfWorkについて言及しています。

this の回答からDalSessionおよびUnitOfWorkを参照してください。これは接続を処理します。
this 回答からBaseDalを参照してください。これはRepository(実際にはBaseRepository)の実装です。

  • UnitOfWorkは一時的なものとして挿入されます。
  • 複数のデータソースは、データソースごとに個別のDalSessionを作成することで処理できます。
  • UnitOfWorkBaseDalに挿入されます。

Dapperを使用する場合のSqlConnectionオブジェクトの有効期間に関する推奨事項/ベストプラクティスはありますか?

ほとんどの開発者が同意することの1つは、接続は可能な限り短期間である必要があるということです。ここには2つのアプローチがあります。

  1. アクションごとの接続。
    このコースは、接続の最短寿命になります。アクションごとにusingブロックで接続を囲みます。アクションをグループ化したくない限り、これは良いアプローチです。アクションをグループ化したい場合でも、ほとんどの場合トランザクションを使用できます。
    問題は、複数のクラス/メソッドにまたがってアクションをグループ化したい場合です。ここではusingブロックを使用できません。ソリューションは以下のUnitOfWorkです。
  2. 作業単位ごとの接続。
    作業単位を定義します。これはアプリケーションごとに異なります。 Webアプリケーションでは、「リクエストごとの接続」が広く使用されているアプローチです。
    通常、全体として実行するアクションのグループが(ほとんどの場合)存在するため、これはより理にかなっています。これは、上記で提供した2つのリンクで説明されています。
    このアプローチのもう1つの利点は、(DALを使用する)アプリケーションが、接続の使用方法をより詳細に制御できることです。そして、私の理解では、アプリケーションはDALよりも接続の使用方法をよく理解しています。
4
Amit Joshi

すばらしい質問で、すでに2つのすばらしい答えがあります。私は最初これに戸惑い、リポジトリをマネージャーにカプセル化するという問題を解決する次の解決策を思いつきました。マネージャ自体は、接続文字列を抽出し、それをリポジトリに挿入する責任があります。

私はこの方法を使用して、リポジトリを個別にテストできるようにしました。たとえば、モックコンソールアプリを使用すると、はるかに簡単になり、いくつかの大規模プロジェクトでこのパターンをたどることができました。私は確かにテスト、依存性注入、または実際には何も専門家ではありません!

私が私に残した主な質問は、DbServiceをシングルトンにするかどうかです。私の論理的根拠は、DbServiceにカプセル化されたさまざまなリポジトリを常に作成および破棄することはあまり意味がなかったことであり、それらはすべてステートレスであるため、それらを「ライブ」にすることに大きな問題は見られませんでした。ただし、これは完全に無効なロジックである可能性があります。

編集:既製のソリューションが必要な場合は、 GitHub で私のDapperリポジトリ実装を確認してください

リポジトリマネージャは次のように構成されています。

/*
 * Db Service
 */
public interface IDbService
{
    ISomeRepo SomeRepo { get; }
}

public class DbService : IDbService
{
    readonly string connStr;
    ISomeRepo someRepo;

    public DbService(string connStr)
    {
        this.connStr = connStr;
    }

    public ISomeRepo SomeRepo
    {
        get
        {
            if (someRepo == null)
            {
                someRepo = new SomeRepo(this.connStr);
            }

            return someRepo;
        }
    }
}

サンプルリポジトリは次のように構成されます。

/*
 * Mock Repo
 */
public interface ISomeRepo
{
    IEnumerable<SomeModel> List();
}

public class SomeRepo : ISomeRepo
{
    readonly string connStr;

    public SomeRepo(string connStr)
    {
        this.connStr = connStr;
    }

    public IEnumerable<SomeModel> List()
    {
        //work to return list of SomeModel 
    }
}

すべてを配線する:

/*
 * Startup.cs
 */
public IConfigurationRoot Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    //...rest of services

    services.AddSingleton<IDbService, DbService>();

    //...rest of services
}

そして最後に、それを使用します:

public SomeController : Controller 
{
    IDbService dbService;

    public SomeController(IDbService dbService)
    {
        this.dbService = dbService;
    }

    public IActionResult Index()
    {
        return View(dbService.SomeRepo.List());
    }
}
1
robopim