Entity Framework cambia connessione in fase di esecuzione

Ho un progetto API Web che fa riferimento al mio modello e agli assemblaggi DAL. All’utente viene presentata una schermata di accesso, in cui è ansible selezionare diversi database.

Costruisco la stringa di connessione come segue:

public void Connect(Database database) { //Build an SQL connection string SqlConnectionStringBuilder sqlString = new SqlConnectionStringBuilder() { DataSource = database.Server, InitialCatalog = database.Catalog, UserID = database.Username, Password = database.Password, }; //Build an entity framework connection string EntityConnectionStringBuilder entityString = new EntityConnectionStringBuilder() { Provider = database.Provider, Metadata = Settings.Default.Metadata, ProviderConnectionString = sqlString.ToString() }; } 

Prima di tutto, come faccio a cambiare la connessione del contesto dati?

E in secondo luogo, dato che questo è un progetto di API Web, la stringa di connessione (impostata all’accesso per sopra) è persistente nell’interazione dell’utente o dovrebbe essere passata ogni volta al mio contesto dati?

Un po ‘in ritardo su questa risposta, ma penso che ci sia un modo potenziale per farlo con un metodo di estensione piccolo e accurato. Possiamo trarre vantaggio dalla convenzione EF sulla configurazione più alcune poche chiamate quadro.

Ad ogni modo, il codice commentato e l’utilizzo di esempio:

class metodo di estensione:

 public static class ConnectionTools { // all params are optional public static void ChangeDatabase( this DbContext source, string initialCatalog = "", string dataSource = "", string userId = "", string password = "", bool integratedSecuity = true, string configConnectionStringName = "") /* this would be used if the * connectionString name varied from * the base EF class name */ { try { // use the const name if it's not null, otherwise // using the convention of connection string = EF contextname // grab the type name and we're done var configNameEf = string.IsNullOrEmpty(configConnectionStringName) ? source.GetType().Name : configConnectionStringName; // add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder (System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString); // init the sqlbuilder with the full EF connectionstring cargo var sqlCnxStringBuilder = new SqlConnectionStringBuilder (entityCnxStringBuilder.ProviderConnectionString); // only populate parameters with values if added if (!string.IsNullOrEmpty(initialCatalog)) sqlCnxStringBuilder.InitialCatalog = initialCatalog; if (!string.IsNullOrEmpty(dataSource)) sqlCnxStringBuilder.DataSource = dataSource; if (!string.IsNullOrEmpty(userId)) sqlCnxStringBuilder.UserID = userId; if (!string.IsNullOrEmpty(password)) sqlCnxStringBuilder.Password = password; // set the integrated security status sqlCnxStringBuilder.IntegratedSecurity = integratedSecuity; // now flip the properties that were changed source.Database.Connection.ConnectionString = sqlCnxStringBuilder.ConnectionString; } catch (Exception ex) { // set log item if required } } } 

uso di base:

 // assumes a connectionString name in .config of MyDbEntities var selectedDb = new MyDbEntities(); // so only reference the changed properties // using the object parameters by name selectedDb.ChangeDatabase ( initialCatalog: "name-of-another-initialcatalog", userId: "jackthelady", password: "nomoresecrets", dataSource: @".\sqlexpress" // could be ip address 120.273.435.167 etc ); 

So che hai già la funzionalità di base in atto, ma ho pensato che questo avrebbe aggiunto un po ‘di diversità.

DbContext ha un overload di costruttore che accetta il nome di una stringa di connessione o una stringa di connessione stessa. Implementa la tua versione e passala al costruttore base:

 public class MyDbContext : DbContext { public MyDbContext( string nameOrConnectionString ) : base( nameOrConnectionString ) { } } 

Quindi basta passare il nome di una stringa di connessione configurata o una stringa di connessione stessa quando si crea un’istanza di DbContext

 var context = new MyDbContext( "..." ); 

La risposta di Jim Tollan funziona alla grande, ma ho ricevuto l’errore: Parola chiave non supportata ‘origine dati’. Per risolvere questo problema ho dovuto cambiare questa parte del suo codice:

 // add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder (System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString); 

a questa:

 // add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder { ProviderConnectionString = new SqlConnectionStringBuilder(System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString).ConnectionString }; 

Sono davvero dispiaciuto. So che non dovrei usare le risposte per rispondere ad altre risposte, ma la mia risposta è troppo lunga per un commento 🙁

La class creata è “parziale”!

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

… e lo chiami così:

 using (var ctx = new Database1Entities1()) { #if DEBUG ctx.Database.Log = Console.Write; #endif 

quindi, è sufficiente creare un file di class proprio parziale per la class auto-generata originale (con lo stesso nome di class!) e aggiungere un nuovo costruttore con parametro stringa di connessione, come la risposta di Moho prima.

Dopo di esso è ansible utilizzare il costruttore parametrizzato rispetto all’originale. 🙂

esempio:

 using (var ctx = new Database1Entities1(myOwnConnectionString)) { #if DEBUG ctx.Database.Log = Console.Write; #endif 

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

Quindi puoi ottenerli come una stringa come:

 System.Configuration.ConfigurationManager. ConnectionStrings["entityFrameworkConnection"].ConnectionString; 

Quindi usa la stringa per impostare:

 Provider Metadata ProviderConnectionString 

È meglio spiegato qui:

Leggi la stringa di connessione da web.config

 string _connString = "metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string="data source=localhost;initial catalog=DATABASE;persist security info=True;user id=sa;password=YourPassword;multipleactiveresultsets=True;App=EntityFramework""; EntityConnectionStringBuilder ecsb = new EntityConnectionStringBuilder(_connString); ctx = new Entities(_connString); 

È ansible ottenere la stringa di connessione da web.config e impostarla semplicemente nel costruttore EntityConnectionStringBuilder e utilizzare EntityConnectionStringBuilder come argomento nel costruttore per il contesto.

Cache la stringa di connessione per nome utente. Semplice esempio utilizzando un paio di metodi generici per gestire l’aggiunta / il recupero dalla cache.

 private static readonly ObjectCache cache = MemoryCache.Default; // add to cache AddToCache(username, value); // get from cache string value = GetFromCache(username); if (value != null) { // got item, do something with it. } else { // item does not exist in cache. } public void AddToCache(string token, T item) { cache.Add(token, item, DateTime.Now.AddMinutes(1)); } public T GetFromCache(string cacheKey) where T : class { try { return (T)cache[cacheKey]; } catch { return null; } } 

Nel mio caso sto usando ObjectContext in contrasto con DbContext, quindi ho ottimizzato il codice nella risposta accettata per quello scopo.

 public static class ConnectionTools { public static void ChangeDatabase( this ObjectContext source, string initialCatalog = "", string dataSource = "", string userId = "", string password = "", bool integratedSecuity = true, string configConnectionStringName = "") { try { // use the const name if it's not null, otherwise // using the convention of connection string = EF contextname // grab the type name and we're done var configNameEf = string.IsNullOrEmpty(configConnectionStringName) ? Source.GetType().Name : configConnectionStringName; // add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder (System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString); // init the sqlbuilder with the full EF connectionstring cargo var sqlCnxStringBuilder = new SqlConnectionStringBuilder (entityCnxStringBuilder.ProviderConnectionString); // only populate parameters with values if added if (!string.IsNullOrEmpty(initialCatalog)) sqlCnxStringBuilder.InitialCatalog = initialCatalog; if (!string.IsNullOrEmpty(dataSource)) sqlCnxStringBuilder.DataSource = dataSource; if (!string.IsNullOrEmpty(userId)) sqlCnxStringBuilder.UserID = userId; if (!string.IsNullOrEmpty(password)) sqlCnxStringBuilder.Password = password; // set the integrated security status sqlCnxStringBuilder.IntegratedSecurity = integratedSecuity; // now flip the properties that were changed source.Connection.ConnectionString = sqlCnxStringBuilder.ConnectionString; } catch (Exception ex) { // set log item if required } } } 

Volevo avere più origini dati nella configurazione dell’app. Quindi, dopo aver impostato una sezione nell’app.config, ho scambiato l’origine dati e poi la ho passata al dbcontext come stringa di connessione.

 //Get the key/value connection string from app config var sect = (NameValueCollection)ConfigurationManager.GetSection("section"); var val = sect["New DataSource"].ToString(); //Get the original connection string with the full payload var entityCnxStringBuilder = new EntityConnectionStringBuilder(ConfigurationManager.ConnectionStrings["OriginalStringBuiltByADO.Net"].ConnectionString); //Swap out the provider specific connection string entityCnxStringBuilder.ProviderConnectionString = val; //Return the payload with the change in connection string. return entityCnxStringBuilder.ConnectionString; 

Questo mi ha portato un po ‘a capire. Spero che aiuti qualcuno. Lo stavo rendendo troppo complicato. prima di questo.

Ho due metodi di estensione per convertire la normale stringa di connessione nel formato Entity Framework. Questa versione funziona bene con i progetti di libreria di classi senza copiare le stringhe di connessione dal file app.config al progetto principale. Questo è VB.Net ma facile da convertire in C #.

 Public Module Extensions  Public Function ToEntityConnectionString(ByRef sqlClientConnStr As String, ByVal modelFileName As String, Optional ByVal multipleActiceResultSet As Boolean = True) Dim sqlb As New SqlConnectionStringBuilder(sqlClientConnStr) Return ToEntityConnectionString(sqlb, modelFileName, multipleActiceResultSet) End Function  Public Function ToEntityConnectionString(ByRef sqlClientConnStrBldr As SqlConnectionStringBuilder, ByVal modelFileName As String, Optional ByVal multipleActiceResultSet As Boolean = True) sqlClientConnStrBldr.MultipleActiveResultSets = multipleActiceResultSet sqlClientConnStrBldr.ApplicationName = "EntityFramework" Dim metaData As String = "metadata=res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl;provider=System.Data.SqlClient;provider connection string='{1}'" Return String.Format(metaData, modelFileName, sqlClientConnStrBldr.ConnectionString) End Function End Module 

Dopo di che creo una class parziale per DbContext:

 Partial Public Class DlmsDataContext Public Shared Property ModelFileName As String = "AvrEntities" ' (AvrEntities.edmx) Public Sub New(ByVal avrConnectionString As String) MyBase.New(CStr(avrConnectionString.ToEntityConnectionString(ModelFileName, True))) End Sub End Class 

Creare una query:

 Dim newConnectionString As String = "Data Source=.\SQLEXPRESS;Initial Catalog=DB;Persist Security Info=True;User ID=sa;Password=pass" Using ctx As New DlmsDataContext(newConnectionString) ' ... ctx.SaveChanges() End Using 
 Linq2SQLDataClassesDataContext db = new Linq2SQLDataClassesDataContext(); var query = from p in db.SyncAudits orderby p.SyncTime descending select p; Console.WriteLine(query.ToString()); 

prova questo codice …