Tengo un proyecto de 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()
};
}
En primer lugar, ¿cómo cambio realmente la conexión del contexto de datos?
Y en segundo lugar, como se trata de un proyecto de API web, ¿la cadena de conexión (configurada en el inicio de sesión anterior) es persistente a lo largo de la interacción del usuario o debería pasarse cada vez a mi contexto de datos?
c#
entity-framework
asp.net-web-api
connection-string
Ivan-Mark Debono
fuente
fuente
Respuestas:
Un poco tarde en esta respuesta, pero creo que hay una forma potencial de hacer esto con un pequeño método de extensión. Podemos aprovechar la convención EF sobre la configuración más algunas pequeñas llamadas al marco.
De todos modos, el código comentado y el uso de 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 tiene la funcionalidad básica en su lugar, pero pensé que esto agregaría un poco de diversidad.
fuente
Controller
que siempre establecerá el 'db' del controlador en su db específico del cliente. Esto también me libera a mí (oa cualquier administrador / desarrollador futuro) de tener que crear una nueva cadena de conexión para cada cliente que se agrega.// add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder { ProviderConnectionString = new SqlConnectionStringBuilder(System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString).ConnectionString };
DbContext
tiene una sobrecarga de constructor que acepta el nombre de una cadena de conexión o una cadena de conexión en sí. Implementa tu propia versión y pásala al constructor base:public class MyDbContext : DbContext { public MyDbContext( string nameOrConnectionString ) : base( nameOrConnectionString ) { } }
Luego, simplemente pase el nombre de una cadena de conexión configurada o una cadena de conexión cuando cree una instancia de su
DbContext
var context = new MyDbContext( "..." );
fuente
La respuesta de Jim Tollan funciona muy bien, pero recibí el Error: palabra clave no admitida '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 debería usar respuestas para responder a otras respuestas, pero mi respuesta es demasiado larga para un comentario :(
fuente
¡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 parámetro de cadena de conexión, como la respuesta de Moho antes.
Después de eso, puede usar el constructor parametrizado contra el original. :-)
ejemplo:
using (var ctx = new Database1Entities1(myOwnConnectionString)) { #if DEBUG ctx.Database.Log = Console.Write; #endif
fuente
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:
Se explica mejor aquí:
Leer la cadena de conexión de web.config
fuente
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 de web.config, y simplemente establecerla en el constructor EntityConnectionStringBuilder, y usar EntityConnectionStringBuilder como argumento en el constructor para el contexto.
Almacene en caché la cadena de conexión por nombre de usuario. Ejemplo simple usando un par de métodos genéricos para manejar la adición / recuperación de caché.
private static readonly ObjectCache cache = MemoryCache.Default; // add to cache AddToCache<string>(username, value); // get from cache string value = GetFromCache<string>(username); if (value != null) { // got item, do something with it. } else { // item does not exist in cache. } public void AddToCache<T>(string token, T item) { cache.Add(token, item, DateTime.Now.AddMinutes(1)); } public T GetFromCache<T>(string cacheKey) where T : class { try { return (T)cache[cacheKey]; } catch { return null; } }
fuente
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 } } }
fuente
Quería tener múltiples fuentes de datos en la configuración de la aplicación. Entonces, después de configurar una sección en app.config, cambié la fuente de datos y luego la paso al dbcontext 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 entender. Espero que ayude a alguien. Lo estaba haciendo demasiado complicado. antes de esto.
fuente
Tengo dos métodos de extensión para convertir la cadena de conexión normal al formato Entity Framework. Esta versión funciona bien con proyectos de biblioteca de clases 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 <Extension> 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 <Extension> 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
fuente
Tanto para SQL Server como para bases de datos SQLite, use:
_sqlServerDBsContext = new SqlServerDBsContext(new DbContextOptionsBuilder<SqlServerDBsContext>().UseSqlServer("Connection String to SQL DB").Options);
_sqliteDBsContext = new SqliteDBsContext(new DbContextOptionsBuilder<SqliteDBsContext>().UseSqlite("Connection String to SQLite DB").Options);
fuente
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 ...
fuente