web-dev-qa-db-ja.com

Entity Framework:実行時に接続文字列を変更する

Entity Framework 6を​​コードファーストアプローチで使用し、StructureMapをIoCとして使用するASP.NET MVCアプリケーションがあると仮定します。
また、作業単位パターンを使用します。コードは次のとおりです。

ドメインクラス

    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }        
 
    }

IUnitOfWorkおよびDbContext:

    public interface IUnitOfWork
{
    IDbSet<TEntity> Set<TEntity>() where TEntity : class;
    int SaveChanges();
}

public class Sample07Context : DbContext, IUnitOfWork
{
    public DbSet<Product> Products { set; get; }

    #region IUnitOfWork Members

    public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
    {
        return base.Set<TEntity>();
    }

    #endregion
}

サービスクラスのビジネスロジック:

   public interface IProductService
{
    void AddNewProduct(Product product);
    IList<Product> GetAllProducts();
}


    public class ProductService : IProductService
    {
        IUnitOfWork _uow;
        IDbSet<Product> _products;
        public ProductService(IUnitOfWork uow)
        {
            _uow = uow;
            _products = _uow.Set<Product>();
        }
 
        public void AddNewProduct(Product product)
        {
            _products.Add(product);
        }
 
        public IList<Product> GetAllProducts()
        {
            return _products.Include(x => x.Category).ToList();
        }
    }

コントローラにサービスクラスを挿入する

        public class HomeController : Controller
    {
        private IProductService _productService;
        private IUnitOfWork _uow;

        public HomeController(IUnitOfWork uow, IProductService productService)
        {
            _productService = productService;

            _uow = uow;
        }

        [HttpGet]
        public ActionResult Index()
        {
            var list = _productService.GetAllProducts();
            return View(list);
        }
    }

App_startで呼び出すStructureMap構成:

       private static void initStructureMap()
        {
            ObjectFactory.Initialize(x =>
            {
                x.For<IUnitOfWork>().HttpContextScoped().Use(() => new Sample07Context());
                x.ForRequestedType<IProductService>().TheDefaultIsConcreteType<EfProductService>();
            });
            //Set current Controller factory as StructureMapControllerFactory
            ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
        }

すべてが単一のデータベースで正常に機能しますが、私のシナリオではユーザーは複数のデータベースを使用できます。つまり、ユーザーは実行時に接続文字列を変更できるはずです。ユーザーがアプリケーションで作成するプロジェクトごとに個別のデータベースを作成します。
問題は、DbContextをサービスに挿入し、DbContextがweb.configから接続文字列を読み取るため、ユーザーがデータベースを変更したときにDbContextに新しい接続文字列を設定できないことです。
何を指示してるんですか?

18
Shahin

私の経験では、EF 6でDatabase Firstモードを使用しました。Entity Data Modelを追加すると、DbContextが以下のように生成されます。

public TestEntities()
            : base("name=TestEntities")
        {
        }

TestEntitiesは、App.ConfigのConnectionString要素を表します

<connectionStrings>   
<add name="TestEntities" connectionString="..." providerName="System.Data.EntityClient" />
</connectionStrings>

ただし、デフォルトのコードを以下に変更できます。

public partial class TestEntities : DbContext
    {
        public TestEntities()
            : base("name=TestEntities")
        {
        }

        public TestEntities(string sConnectionString)
            : base(sConnectionString)
        {
        }

...}

したがって、DB接続を取得するには2つのオプションがあります。

  1. デフォルトを使用します。 EFは、構成ファイルで接続文字列を見つけます。

  2. 接続文字列をDbContextに渡します。

コードは次のようになります。

EntityConnection entityConn =DBConnectionHelper.BuildConnection();
using (var db = new TestEntities(entityConn.ConnectionString))
{
....
}

質問についてHow to build a EntityConnection?MSDN EntityConnection をご覧ください。

お役に立てば幸いです。

ありがとう。

17
Joe.wang

既定では、Entity Frameworkで使用する接続文字列の名前は、DbContextクラスの名前から推測されます。ただし、canコンストラクターパラメーターとして接続文字列を渡します。

public class MyDbContext : DbContext, IUnitOfWork
{
    public MyDbContext(string connectionString)
        : base(connectionString)
    {
    }
}

次に、current接続文字列を渡すようにStructureMapを構成できます。

For<IUnitOfWork>().Use(ctx => new MyDbContext(TheConnectionStringToUse));

これは、コードで設定した静的な値、現在のセッションなどから発生する可能性があります。

5
Ben Foster

私は完全に異なる道を提案するつもりです。 web.configに接続文字列が設定されていると仮定すると、web.debug.configとweb.release.config transforrmsを使用して接続文字列を適切に設定しないのはなぜですか?

つまり、web.debug.configで

<connectionStrings>
    <add name="FooEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="Data Source=IP,PORT\Instancename;
    Initial Catalog=Foo;Persist Security Info=True;User ID=admin;Password=password;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient"/>
</connectionStrings>

web.release.configなど

<connectionStrings xdt:Transform="Replace">
    <add name="FooEntities" connectionString="metadata=res://*/;provider=System.Data.SqlClient;provider connection string="Data Source=LIVEIP,PORT\Instancename;
    Initial Catalog=Foo;Persist Security Info=True;User ID=admin;Password=password;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient"/>
</connectionStrings>

非常に詳細な説明が利用可能です こちら

0
PlTaylor