Entity Framework change connection at runtime
我有一个Web API项目,它引用我的模型和DAL程序集。用户将看到一个登录屏幕,从中可以选择不同的数据库。
我构建连接字符串如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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() }; } |
首先,如何实际更改数据上下文的连接?
其次,由于这是一个Web API项目,连接字符串(设置为上面的登录)是在整个用户交互过程中持续存在的,还是应该每次传递到我的数据上下文?
这个答案有点晚了,但我认为有一种可能的方法可以用一个简洁的小扩展方法来实现这一点。我们可以利用ef约定胜过配置,再加上一些框架调用。
总之,注释代码和示例用法:
扩展方法类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | 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 } } } |
基本用法:
1 2 3 4 5 6 7 8 9 10 11 | // 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 ); |
我知道你已经具备了基本的功能,但是我认为这会增加一些多样性。
1 2 3 4 5 6 7 | public class MyDbContext : DbContext { public MyDbContext( string nameOrConnectionString ) : base( nameOrConnectionString ) { } } |
然后在实例化
1 |
吉姆·托兰的回答很好,但我得到了一个错误:关键字不支持"数据源"。为了解决这个问题,我必须更改他的代码的这一部分:
1 2 3 4 | // add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder (System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString); |
对此:
1 2 3 4 5 6 |
我真的很抱歉。我知道我不应该用答案来回答其他答案,但我的答案太长,无法发表评论:(
创建的类是"部分"!
1 2 3 4 5 6 | public partial class Database1Entities1 : DbContext { public Database1Entities1() : base("name=Database1Entities1") { } |
…你这样称呼它:
1 2 3 4 5 |
因此,您只需要为原始自动生成的类(具有相同的类名!)创建一个部分的自己的类文件。并添加一个带有连接字符串参数的新构造函数,就像之前的moho答案一样。
之后,您可以使用参数化的构造函数来对抗原始的。-)
例子:
1 2 3 4 5 | using (var ctx = new Database1Entities1(myOwnConnectionString)) { #if DEBUG ctx.Database.Log = Console.Write; #endif |
我有两种扩展方法可以将普通连接字符串转换为实体框架格式。这个版本可以很好地处理类库项目,而无需将连接字符串从app.config文件复制到主项目。这是vb.net,但很容易转换为c。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 |
之后,我为dbContext创建一个分部类:
1 2 3 4 5 6 7 8 9 | 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 |
创建查询:
1 2 3 4 5 6 | 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 |
我想在应用程序配置中有多个数据源。因此,在app.config中设置了一个节之后,我提取了数据源,然后将其作为连接字符串传递到dbcontext中。
1 2 3 4 5 6 7 8 9 10 11 12 | //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; |
这让我有点费解。我希望它能帮助别人。我把事情弄得太复杂了。在此之前。
在我的例子中,我使用的是ObjectContext,而不是dbContext,因此我在接受的答案中调整了代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | 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 } } } |
1 2 3 4 | 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); |
您可以从web.config获取连接字符串,只需在EntityConnectionStringBuilder构造函数中设置该字符串,并将EntityConnectionStringBuilder用作上下文的构造函数中的参数。
按用户名缓存连接字符串。简单的例子,使用一些通用方法来处理从缓存中添加/检索。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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; } } |
在web.config或app.config中添加多个连接字符串。
然后你可以得到一个字符串,比如:
1 2 | System.Configuration.ConfigurationManager. ConnectionStrings["entityFrameworkConnection"].ConnectionString; |
然后使用字符串设置:
1 2 3 | Provider Metadata ProviderConnectionString |
这里最好解释一下:
从web.config读取连接字符串
1 2 3 4 | Linq2SQLDataClassesDataContext db = new Linq2SQLDataClassesDataContext(); var query = from p in db.SyncAudits orderby p.SyncTime descending select p; Console.WriteLine(query.ToString()); |
尝试此代码…