Entity Framework cambia la conexión en tiempo de ejecución

Tengo un proyecto API web que hace referencia a mi modelo y ensamblajes DAL. Al usuario se le presenta una pantalla de inicio de sesión, donde puede seleccionar diferentes bases de datos.

Construyo la cadena de conexión de la siguiente manera:

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

Antes que nada, ¿cómo cambio realmente la conexión del contexto de datos?

Y en segundo lugar, como se trata de un proyecto API web, ¿la cadena de conexión (establecida en el inicio de sesión por arriba) es persistente a lo largo de la interacción del usuario o se debe pasar cada vez a mi contexto de datos?

Un poco tarde en esta respuesta, pero creo que hay una forma potencial de hacerlo con un pequeño y prolijo método de extensión. Podemos aprovechar la convención de EF sobre la configuración más unas pocas llamadas al framework.

De todos modos, el código comentado y el uso del ejemplo:

clase de método de extensión:

 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 básico:

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

Sé que ya tienes la funcionalidad básica en su lugar, pero pensé que esto agregaría un poco de diversidad.

DbContext tiene una sobrecarga de constructor que acepta el nombre de una cadena de conexión o una cadena de conexión. Implementa tu propia versión y pásala al constructor base:

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

A continuación, simplemente pase el nombre de una cadena de conexión configurada o una cadena de conexión en sí cuando DbContext una instancia de su DbContext

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

La respuesta de Jim Tollan funciona muy bien, pero recibí el error: Palabra clave no compatible ‘fuente de datos’. Para resolver este problema, tuve que cambiar esta parte de su código:

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

a esto:

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

Lo siento mucho. Sé que no debo usar las respuestas para responder a otras respuestas, pero mi respuesta es demasiado larga para un comentario 🙁

¡La clase creada es ‘parcial’!

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

… y lo llamas así:

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

por lo tanto, solo necesita crear un archivo de clase propio parcial para la clase original generada automáticamente (¡con el mismo nombre de clase!) y agregar un nuevo constructor con el parámetro de cadena de conexión, como la respuesta de Moho anterior.

Después puede usar el constructor parametrizado contra el original. 🙂

ejemplo:

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

Agregue varias cadenas de conexión en su web.config o app.config.

Entonces puedes obtenerlos como una cadena como:

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

Luego usa la cadena para establecer:

 Provider Metadata ProviderConnectionString 

Se explica mejor aquí:

Leer cadena de conexión de 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); 

Puede obtener la cadena de conexión del web.config, y simplemente configurar eso en el constructor EntityConnectionStringBuilder, y usar EntityConnectionStringBuilder como un argumento en el constructor para el contexto.

Guarde en caché la cadena de conexión por nombre de usuario. Ejemplo simple usando un par de métodos generics para manejar la adición / recuperación de la memoria caché.

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

En mi caso, estoy usando ObjectContext en lugar de DbContext, así que modifiqué el código en la respuesta aceptada para ese propósito.

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

Quería tener múltiples fonts de datos en la configuración de la aplicación. Entonces, después de configurar una sección en el app.config, cambié el origen de datos y luego lo pasé al contexto dbcontexto como la cadena de conexión.

 //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; 

Esto me tomó un poco descubrirlo. Espero que ayude a alguien a salir. Lo estaba haciendo demasiado complicado. antes de esto.

Tengo dos métodos de extensión para convertir la cadena de conexión normal al formato de Entity Framework. Esta versión funciona bien con proyectos de biblioteca de clase sin copiar las cadenas de conexión del archivo app.config al proyecto principal. Esto es VB.Net pero fácil de convertir a 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 

Después de eso creo una clase parcial para 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 

Creando una consulta:

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

prueba este código …