Entity Framework: modifica la stringa di connessione in fase di runtime

Supponendo che esista un’applicazione ASP.NET MVC che utilizza Entity Framework 6 con approccio code-first e StructureMap come IoC.
Inoltre utilizza pattern Unità di lavoro. Ecco i codici:

Classe di dominio

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

IUnitOfWork e DbContext:

  public interface IUnitOfWork { IDbSet Set() where TEntity : class; int SaveChanges(); } public class Sample07Context : DbContext, IUnitOfWork { public DbSet Products { set; get; } #region IUnitOfWork Members public new IDbSet Set() where TEntity : class { return base.Set(); } #endregion } 

Logica di business nelle classi di servizio:

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

Iniezione della class di servizio nel controller

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

Configurazione di StructureMap che chiamiamo in app_start:

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

Tutto funziona bene con un singolo database ma nel mio scenario l’utente può utilizzare più database, voglio dire che l’utente dovrebbe essere in grado di cambiare la stringa di connessione in fase di esecuzione. Creiamo database separati per ogni progetto che l’utente crea nell’applicazione.
Ora il problema è che iniettiamo DbContext per il servizio e DbContext legge la stringa di connessione da web.config così quando l’utente cambia il database non possiamo impostare una nuova stringa di connessione su DbContext.
Cosa suggerisci?

Nella mia esperienza, ho usato la modalità Database First in EF 6. Il DbContext sarebbe stato generato come di seguito quando aggiungo Entity Data Model .

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

TestEntities rappresenta l’elemento ConnectionString in App.Config

    

Ma puoi cambiare il codice predefinito in basso.

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

Quindi hai due opzioni per ottenere la connessione DB.

  1. usando il valore predefinito. L’EF troverà la stringa di connessione nel file di configurazione.

  2. passando la stringa di connessione a DbContext.

Il codice appare come di seguito.

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

Per quanto riguarda la domanda How to build a EntityConnection? . Vedere MSDN EntityConnection .

Spero che sia utile

Grazie.

Per impostazione predefinita, il nome della stringa di connessione da utilizzare in Entity Framework viene dedotto dal nome della class DbContext . Tuttavia puoi passare la stringa di connessione come parametro costruttore:

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

Quindi è ansible configurare StructureMap per passare la stringa di connessione corrente ad es

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

Ciò potrebbe provenire da un valore statico impostato nel codice, nella sessione corrente, ecc.

Ho intenzione di suggerire un percorso completamente diverso. Supponendo che tu abbia le stringhe di connessione configurate nel tuo web.config, che tu dici di fare, perché non dovresti usare i transforr di web.debug.config e web.release.config per settare le tue stringhe di connessione in modo appropriato?

cioè in web.debug.config

    

e un web.release.config come tale

    

Una spiegazione molto approfondita è disponibile qui

  public partial class YourDBContextClass { // Add a constructor to allow the connection string name to be changed public YourDBContextClass(string connectionStringNameInConfig) : base("name=" + connectionStringNameInConfig) { } } 

Aggiungi più stringhe di connessione al tuo file web o app.config.

nel tuo codice di programma:

 YourDBContextClass dbcontext = new YourDBContextClass("alternateconnectionstringname"); 

I due approcci sono validi per due diverse situazioni:

  • La trasformazione è buona per la distribuzione di una stringa di connessione che cambia solo per i diversi ambienti (test, produzione).

  • L’approccio di aggiungere un costruttore (che prende il nome della stringa di connessione) in un file separato per estendere la class dbcontext parziale consente di commutare la connessione in fase di runtime.

Aggiungi due diverse stringhe di connessione nel file App.Config utilizzando un nome diverso.

Imposta la connessione corrente Nome stringa in Entity Constructor usando Overloading.

Nel file di codice

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

Quando passiamo la stringa con l’object, viene utilizzata una stringa di connessione diversa.