Entity Framework: изменение строки подключения во время выполнения
Предполагая, что есть приложение ASP.NET MVC, которое использует Entity Framework 6 с кодовым подходом и StructureMap как IoC.
Кроме того, он использует шаблон Unit Of Work.
Вот коды:
Класс домена
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);
}
}
Конфигурация StructureMap, которую мы вызываем в 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, поэтому, когда пользователь меняет базу данных, мы не можем установить новую строку подключения для DbContext.
Что вы предлагаете?
Ответы
Ответ 1
По моему опыту, я использовал режим Database First
в EF 6. DbContext
будет генерироваться, как показано ниже, когда я добавляю Entity Data Model
.
public TestEntities()
: base("name=TestEntities")
{
}
TestEntities
представляет элемент ConnectionString
в App.Config
<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.
Код выглядит следующим образом.
EntityConnection entityConn =DBConnectionHelper.BuildConnection();
using (var db = new TestEntities(entityConn.ConnectionString))
{
....
}
Что касается вопроса How to build a EntityConnection?
. См. MSDN EntityConnection.
Надеюсь, это полезно.
Спасибо.
Ответ 2
По умолчанию имя строки подключения для использования в Entity Framework выводится из имени вашего класса DbContext
. Однако вы можете передать строку подключения в качестве параметра конструктора:
public class MyDbContext : DbContext, IUnitOfWork
{
public MyDbContext(string connectionString)
: base(connectionString)
{
}
}
Затем вы можете настроить StructureMap для передачи в текущей строке соединения, например.
For<IUnitOfWork>().Use(ctx => new MyDbContext(TheConnectionStringToUse));
Это может исходить от статического значения, которое вы задали в своем коде, текущем сеансе и т.д.
Ответ 3
Я собираюсь предложить совершенно другой путь. Предполагая, что у вас есть строки подключения, установленные в вашем 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>
Существует очень подробное объяснение здесь
Ответ 4
public partial class YourDBContextClass
{
// Add a constructor to allow the connection string name to be changed
public YourDBContextClass(string connectionStringNameInConfig)
: base("name=" + connectionStringNameInConfig)
{
}
}
Добавьте несколько строк подключения в свой веб файл или файл app.config.
в вашем программном коде:
YourDBContextClass dbcontext = new YourDBContextClass("alternateconnectionstringname");
Ответ 5
Два подхода хороши для двух разных ситуаций:
-
Преобразование полезно для развертывания строки подключения, которая изменяется только для разных версий (тест, производство).
-
Подход добавления конструктора (который принимает имя строки подключения) в отдельном файле для расширения частичного класса dbcontext позволяет переключать соединение во время выполнения.
Ответ 6
Добавьте две разные строки подключения в файл App.Config, используя другое имя.
Установить текущее имя строки подключения в конструкторе Entity с помощью перегрузки.
В файле кода
public ASM_DBEntities()
: base("name=ASM_DBEntities")
{
}
public ASM_DBEntities(string conn)
: base("name=ASM_DBEntities1")
{
}
Когда мы передаем строку с объектом, используется другая строка соединения.