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

Sha*_*hin 18 c# structuremap asp.net-mvc entity-framework dependency-injection

假设有一个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; }        
 
    }
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

服务类中的业务逻辑:

   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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

在控制器中注入服务类

        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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

我们在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());
        }
Run Code Online (Sandbox Code Playgroud)

单个数据库一切正常,但在我的场景中,用户可以使用多个数据库,我的意思是用户应该能够在运行时更改连接字符串.我们为用户在应用程序中创建的每个项目创建单独的数据库.
现在问题是我们将DbContext注入服务并且DbContext从web.config读取连接字符串,因此当用户更改数据库时,我们无法将新连接字符串设置为DbContext.
你有什么建议?

Joe*_*ang 17

根据我的经验,我使用了Database FirstEF 6中的模式.DbContext当我添加时,将生成如下所示Entity Data Model.

public TestEntities()
            : base("name=TestEntities")
        {
        }
Run Code Online (Sandbox Code Playgroud)

TestEntities代表ConnectionString在App.config元件

<connectionStrings>   
<add name="TestEntities" connectionString="..." providerName="System.Data.EntityClient" />
</connectionStrings>
Run Code Online (Sandbox Code Playgroud)

但您可以将默认代码更改为以下.

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

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

...}
Run Code Online (Sandbox Code Playgroud)

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

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

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

代码如下所示.

EntityConnection entityConn =DBConnectionHelper.BuildConnection();
using (var db = new TestEntities(entityConn.ConnectionString))
{
....
}
Run Code Online (Sandbox Code Playgroud)

至于问题How to build a EntityConnection?.请参阅MSDN EntityConnection.

希望它有所帮助.

谢谢.

  • 不确定与Code首先相关,但是DB首先将添加的方法粘贴在不同的类文件中,因此不会在更新时删除. (4认同)
  • 通过另一个类 - 我的意思是公共部分类,使用将字符串传递给base的方法将其放入其自己的cs文件中,这样如果实体框架CS文件发生更改,添加的运行时集连接字符串构造函数就不会丢失. (3认同)

Ben*_*ter 5

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

public class MyDbContext : DbContext, IUnitOfWork
{
    public MyDbContext(string connectionString)
        : base(connectionString)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

For<IUnitOfWork>().Use(ctx => new MyDbContext(TheConnectionStringToUse));
Run Code Online (Sandbox Code Playgroud)

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


PlT*_*lor 1

我将建议一条完全不同的道路。假设您在 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>
Run Code Online (Sandbox Code Playgroud)

和一个 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>
Run Code Online (Sandbox Code Playgroud)

这里有一个非常详尽的解释