どのようにクラスライブラリプロジェクトでDBContextを適切に定義しますか?

asp.net-web-api c# entity-framework entity-framework-core

質問

私はエンティティフレームワーク7を使用して2つのプロジェクトを作成しました。 1つのプロジェクトはASP.NET 5 Web APIプロジェクトとして、もう1つはクラスライブラリプロジェクト(パッケージ)です。私はすべてのデータアクセスレイヤロジックを格納したいと思います。この方法で、このパッケージを別のレポートプロジェクトに使用できます。私が作るかもしれない他の追加のサービス。

基本的に私のデータライブラリプロジェクトの関数を呼び出すWeb APIプロジェクトのコントローラに簡単な投稿があります。関数がデータベースを開始するとき、それは両方のプロジェクトで定義されていても定義されていないデータベースを示します。


エラー

{"No database providers are configured. Configure a database provider by overriding OnConfiguring in your DbContext class or in the AddDbContext method when setting up services."}

コード

データライブラリプロジェクト:InsertPerson

public int InsertPerson(tbl_Person person)
{
    using (var db = new AppContext())
    {
        try
        {
            db.tbl_Person.Add(person);
            db.SaveChanges();
            return person.PersonID;
        }

        catch
        {
            return 0;
        }
    }
}

構成ファイル

Web APIプロジェクト:Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<AppContext>(options =>
            options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<AppContext>();

    services.AddMvc();
}

Web APIプロジェクト:appsettings.json

{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=localhost;Database=testDB;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Verbose",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

Web APIプロジェクト:project.json

{
  "userSecretsId": "blah,
  "version": "1.0.0-rc1-final",
  "compilationOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "DataLibrary": "",
    "Microsoft.AspNet.Authentication.OAuthBearer": "1.0.0-beta7",
    "Microsoft.AspNet.Http.Abstractions": "1.0.0-rc1-final",
    "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-final",
    "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
    "Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
    "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
    "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
    "Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-final",
    "Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
    "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final",
    "Microsoft.Extensions.Logging.Debug": "1.0.0-rc1-final",
    "Remotion.Linq": "2.0.1"
  },

  "commands": {
    "web": "Microsoft.AspNet.Server.Kestrel"
  },

  "frameworks": {
    "dnx451": { },
    "dnxcore50": { }
  },

  "exclude": [
    "wwwroot",
    "node_modules"
  ],
  "publishExclude": [
    "**.user",
    "**.vspscc"
  ]
}

データライブラリプロジェクト:Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<AppContext>(options =>
            options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<AppContext>();

    services.AddMvc();
}

データライブラリプロジェクト:appsettings.json

{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=localhost;Database=testDB;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Verbose",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

データライブラリプロジェクト:AppContext.cs

namespace DataLibrary
{
    public class AppContext : IdentityDbContext<ApplicationUser>
    {
        public DbSet<tbl_Person> tbl_Person { get; set; }

        public static AppContext Create()
        {
            return new AppContext();
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            new tbl_PersonMap(builder.Entity<tbl_Person>());
            base.OnModelCreating(builder);
        }
    }
}

受け入れられた回答

サンプルコードの問題は、ServiceProviderからAppContextインスタンスを解決するのではなく、AppContextインスタンスを新しく作成していることです。この方法でDbContextを初期化すると、新しいServiceProviderがコンテキスト用に暗黙的に作成され、Startup.csの設定はそのコンテキストによって尊重されません。これは、OnConfiguringでコンテキストを設定し、残りのアプリケーションで依存関係注入に関心がない場合に使用したいパターンです。

このような状況では、コンストラクタインジェクションを介してAppContextを取得することが期待されます。この種のソリューションでは、データアクセス層クラスをServiceCollectionに登録する必要があります。結果はもう少し見えるはずです:

データライブラリプロジェクト:AppContext.cs

namespace DataLibrary
{
    public class AppContext : IdentityDbContext<ApplicationUser>
    {
        public DbSet<tbl_Person> tbl_Person { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            new tbl_PersonMap(builder.Entity<tbl_Person>());
            base.OnModelCreating(builder);
        }
    }
}

データライブラリプロジェクト:PersonH​​elper.cs

namespace DataLibrary
{
    class PersonHelper
    {
        private readonly AppContext db;

        public PersonHelper(AppContext context)
        {
            db = context;
        }

        public int InsertPerson(tbl_Person person)
        {
            try
            {
                db.tbl_Person.Add(person);
                db.SaveChanges();
                return person.PersonID;
            }
            catch
            {
                return 0;
            }
        }
    }
}

Web APIプロジェクト:Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFramework()
        .AddSqlServer()
        .AddDbContext<AppContext>(options =>
            options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<AppContext>();

    services.AddScoped<PersonHelper>();

    services.AddMvc();
}

Web APIプロジェクト:MyController.cs

[Route("api/[controller]")]
public class MyController : Controller
{
    private readonly personHelper helper;

    public MyController(PersonHelper helper)
    {
        this.helper = helper;
    }

    // POST api/my
    [HttpPost]
    public void Post([FromBody]string value)
    {
        var person = new tbl_Person
        {
          // ...
        }

        return helper.InsertPerson(person);
    }
}

データアクセスレイヤークラスのIServiceCollectionに拡張メソッドを追加することも考えられます。特に、より一般的なサービスを追加する場合は、構成の重複を減らすのに役立ちます。


人気のある回答

私はこれを行う方法を見つけましたが、私はまだこれが最良の方法であると確信していません。私の挿入関数を保持するクラスファイルでは、これを追加して、Web APIプロジェクトから呼び出されたときに、Web APIからのappcontextがデータライブラリプロジェクトに渡されるようにしました。それはよりエレガントなアプローチがあるかどうかはわかりません

public class fnCommon
{
    private readonly AppContext db;

    public fnCommon(AppContext context)
    {
        this.db = context;
    }

    public int InsertPerson(tbl_Person person)
    {

        try
        {
            db.tbl_Person.Add(person);
            db.SaveChanges();
            return person.PersonID;
        }

        catch
        {
            return 0;
        }
    }
}


Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ