web-dev-qa-db-ja.com

サービスレイヤーへのDbContextの注入

MyDbContextをデータベースサービスレイヤーMyServiceに挿入する方法を教えてください。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
    services.AddMvc();
}

MyDbContext.cs

public partial class MyDbContext : DbContext
{
    public virtual DbSet<User> User { get; set; }

    public MyDbContext(DbContextOptions<MyDbContext> options)
    :base(options)
    {
    }
}

appsettings.json

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=MyServer;Database=MyDatabase;user id=MyUser;password=MyPassword;"
  }
}

MyService.cs

public class MyService
{
    public User GetUser(string username)
    {
        // Should i remove this call and get the reference from the injection somehow?
        MyDbContext db_context = new MyDbContext(optionsBuilder.Options);
        using (db_context)
        {
            var users = from u in db_context.User where u.WindowsLogin == username select u;
            if (users.Count() == 1)
            {
                return users.First();
            }
            return null;
        }
    }
}

私のGetUserメソッドでは、ここで注入されたMyDbContextを使用することになっていることは知っていますが、それを取得する方法はよくわかりません。パズルのどのピースが欠けていますか?

15
Danny Cullen

Dbcontextを自分で含める必要はありません。ASP.NETコア依存性注入サービスがこれを行います。

スタートアップクラスでサービスとデータベースコンテキストを宣言し、必要なdbcontextをサービスのコンストラクタに配置するだけです。

Startup.cs(必要なサービスライフタイムを選択する必要があります。ここでは、リクエストごとにスコープサービスです):

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
    services.AddMvc();
    services.AddScoped<IMyService, MyService>();
}

サービスクラス:

public class MyService : IMyService
{
    private readonly MyDbContext _context;

    public MyService(MyDbContext ctx){
         _context = ctx;
    }

    public User GetUser(string username)
    {
        var users = from u in _context.User where u.WindowsLogin == username select u;
        if (users.Count() == 1)
        {
            return users.First();
        }
        return null;
    }
}

public interface IMyService
{
    User GetUser(string username);
}

コントローラーでは、同じ方法で使用する必要があるサービス(またはデータベースコンテキスト)を宣言する必要があります。

public class TestController : Controller
{
     private readonly IMyService _myService;

      public TestController(IMyService serv)
      {
           _myService = serv;
      }

      public IActionResult Test()
      {
          return _myService.MyMethod(); // No need to instanciate your service here
      }
}

コントローラーに関する注意:データベースコンテキストやサービスで行うように、スタートアップクラスに追加する必要はありません。コンストラクタを実装するだけです。

.NET Core依存性注入に関する詳細情報が必要な場合、公式ドキュメントは明確で非常に完全です: https://docs.Microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection


NB:startup.csでは、AddScoped行はオプションです。サービスに必要なライフタイムを選択できます。選択できるさまざまなライフタイムがあります。

一時的

一時的なライフタイムサービスは、要求されるたびに作成されます。このライフタイムは、軽量でステートレスなサービスに最適です。

スコープ

スコープ付きライフタイムサービスは、リクエストごとに1回作成されます。

シングルトン

シングルトンライフタイムサービスは、最初に要求されたときに(またはインスタンスを指定してConfigureServicesを実行したときに)作成され、その後のすべての要求は同じインスタンスを使用します。

上記から: https://docs.Microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection


注:ここでの質問ではありませんが、GetUserデータクエリは少し奇妙に見えます。 count()== 1の目標がユーザーのユニシティを確認することである場合、データベースにユニシティ制約を追加するのが良い方法です。 count()== 1の目標が、オブジェクトのnull参照例外を回避するためにデータがあることを確認することである場合、.FirstOrDefault()を使用できます。このメソッドを単純化できます:

public User GetUser(string username) => (from u in _context.User where u.WindowsLogin == username select u).FirstOrDefault();
28
AdrienTorris