实体框架:在运行时更改连接字符串

时间:2014-03-08 10:13:06

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

假设有一个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中调用的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。
你有什么建议?

6 个答案:

答案 0 :(得分:17)

根据我的经验,我在EF 6中使用了Database First模式。当我添加DbContext时,Entity Data Model会生成如下所示。

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。

  3. 代码如下所示。

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

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

    希望它有所帮助。

    感谢。

答案 1 :(得分:5)

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

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

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

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

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

答案 2 :(得分:0)

我将建议一条完全不同的道路。假设您在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>

可以获得非常详尽的解释here

答案 3 :(得分:0)

 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");

答案 4 :(得分:-1)

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

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

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

答案 5 :(得分:-4)

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

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

    

在代码文件

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

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

    }

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