【问题标题】:Entity Framework : Change connection string at runtime实体框架:在运行时更改连接字符串
【发布时间】:2014-03-08 10:13:06
【问题描述】:

假设有一个 ASP.NET MVC 应用程序使用 Entity Framework 6 和代码优先方法和 StructureMap 作为 IoC。
它还使用工作单元模式。 以下是代码:

域类

    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 中调用的结构图配置:

       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 读取连接字符串,因此当用户更改数据库时,我们无法将新的连接字符串设置为数据库上下文。
你有什么建议?

【问题讨论】:

  • 你最后是怎么解决的?! (现在是 2017 年 :))

标签: c# asp.net-mvc entity-framework dependency-injection structuremap


【解决方案1】:

根据我的经验,我在 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)
        {
        }

...}

所以你有两个选择来获得数据库连接。

  1. 使用默认值。 EF 会在配置文件中找到连接字符串。

  2. 将连接字符串传递给 DbContext。

代码如下所示。

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

关于How to build a EntityConnection?的问题。请参阅MSDN EntityConnection。

希望对您有所帮助。

谢谢。

【讨论】:

  • 不确定与 Code first 的相关性,但 DB first,将添加的方法粘贴在不同的类文件中,因此不会在更新时被删除。
  • 由另一个类 - 我的意思是公共部分类,方法将字符串传递到基础放入它自己的 cs 文件中,这样如果实体框架 cs 文件发生更改,则添加运行时设置连接字符串构造函数没有丢失。
  • 伙计,这太棒了。那些部分课程有时会挽救生命! :)
  • 迷宫:我不明白。我将 costructor 添加到 Model1.Context.cs 文件中,并且每当模型更新时,它都会丢失,将带有连接字符串参数的构造函数放在哪里。
【解决方案2】:

默认情况下,在实体框架中使用的连接字符串的名称是从 DbContext 类的名称中推断出来的。但是您可以将连接字符串作为构造函数参数传递:

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

然后您可以配置 StructureMap 以传入 current 连接字符串,例如

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

这可能来自您在代码中设置的静态值、当前会话等。

【讨论】:

  • DBContext 基类构造函数调用名为 nameOrConnectionString 的参数。从 DBContext 继承的 T4 生成的类调用 base,传递 base("name=MyDbContext"),所以你会认为如果你想传递连接字符串,你会使用 base("ConnectionString=" + SomeConnectionString ),但显然,这只会产生一个异常:不支持关键字:'connectionstring'
【解决方案3】:

我将建议一条完全不同的路径。假设你在 web.config 中设置了连接字符串,你说你这样做了,为什么不使用 web.debug.config 和 web.release.config 转换来适当地设置连接字符串?

即在 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>

一个非常详尽的解释是可用的here

【讨论】:

    【解决方案4】:
     public partial class YourDBContextClass
     {
        // Add a constructor to allow the connection string name to be changed
     public YourDBContextClass(string connectionStringNameInConfig)
            : base("name=" + connectionStringNameInConfig)
        {
        }
    }
    

    向您的 web 或 app.config 文件添加多个连接字符串。

    在您的程序代码中:

    YourDBContextClass dbcontext = new YourDBContextClass("alternateconnectionstringname");
    

    【讨论】:

      【解决方案5】:

      这两种方法适用于两种不同的情况:

      • 该转换适用于部署仅针对不同环境(测试、生产)更改的连接字符串。

      • 在单独的文件中添加构造函数(采用连接字符串名称)以扩展部分 dbcontext 类的方法允许在运行时切换连接。

      【讨论】:

        【解决方案6】:

        在 App.Config 文件中使用不同的名称添加两个不同的连接字符串。

        使用重载在实体构造函数中设置当前连接字符串名称。

        在代码文件中

        public ASM_DBEntities()
                : base("name=ASM_DBEntities")
            {
            }
        
            public ASM_DBEntities(string conn)
                : base("name=ASM_DBEntities1")
            {
        
            }
        

        当我们用对象传递字符串时,使用不同的连接字符串。

        【讨论】:

          猜你喜欢
          • 2012-10-08
          • 2012-07-07
          • 2013-12-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-08-18
          • 1970-01-01
          相关资源
          最近更新 更多