Изменение соединения Entity Framework во время выполнения
У меня есть проект Web API, который ссылается на мою модель и сборки DAL. Пользователь с экрана входа в систему, где он может выбрать различные базы данных.
Я строю строку подключения следующим образом:
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()
};
}
прежде всего, как на самом деле изменить соединение контекста данных?
и, во-вторых, поскольку это проект веб-API, строка подключения (устанавливается при входе в систему выше) сохраняется во всем взаимодействии пользователя или должна передается каждый раз в мой контекст данных?
10 ответов
немного поздно на этот ответ, но я думаю, что есть потенциальный способ сделать это с помощью аккуратного метода расширения. Мы можем воспользоваться конвенцией EF над конфигурацией плюс несколько небольших вызовов фреймворка.
в любом случае, прокомментированный код и пример использования:
метод расширения класса:
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
}
}
}
основное использование:
// 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
);
Я знаю, что у вас уже есть основная функциональность на месте, но подумал, что это добавит немного разнообразие.
DbContext
имеет перегрузку конструктора, которая принимает имя строки соединения или самой строки соединения. Реализуйте собственную версию и передайте ее базовому конструктору:
public class MyDbContext : DbContext
{
public MyDbContext( string nameOrConnectionString )
: base( nameOrConnectionString )
{
}
}
затем просто передайте имя настроенной строки подключения или самой строки подключения при создании экземпляра DbContext
var context = new MyDbContext( "..." );
ответ Джима Толлана отлично работает, но я получил ошибку: Ключевое слово не поддерживается "источник данных". Чтобы решить эту проблему, мне пришлось изменить эту часть его кода:
// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
(System.Configuration.ConfigurationManager
.ConnectionStrings[configNameEf].ConnectionString);
для этого:
// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
{
ProviderConnectionString = new SqlConnectionStringBuilder(System.Configuration.ConfigurationManager
.ConnectionStrings[configNameEf].ConnectionString).ConnectionString
};
мне очень жаль. Я знаю, что не должен использовать ответы для ответа на другие ответы, но мой ответ слишком длинный для комментария : (
созданный класс является "частичным"!
public partial class Database1Entities1 : DbContext
{
public Database1Entities1()
: base("name=Database1Entities1")
{
}
... и вы называете это так:--4-->
using (var ctx = new Database1Entities1())
{
#if DEBUG
ctx.Database.Log = Console.Write;
#endif
Итак, вам нужно создать только частично собственные файл класса для оригинальные автоматически сгенерированного класса (с тем же именем класса!) и добавьте новый конструктор с параметром строки соединения, как ответ Moho раньше.
после этого вы можете использовать параметризованный конструктор против оригинала. :-)
пример:
using (var ctx = new Database1Entities1(myOwnConnectionString))
{
#if DEBUG
ctx.Database.Log = Console.Write;
#endif
добавить несколько строк подключения в файле web.config или app.конфиг.
затем вы можете получить их в виде строки, например:
System.Configuration.ConfigurationManager.
ConnectionStrings["entityFrameworkConnection"].ConnectionString;
затем используйте строку для установки:
Provider
Metadata
ProviderConnectionString
это лучше объяснить здесь:
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);
Вы можете получить строку подключения из интернета.config и просто установите это в конструкторе EntityConnectionStringBuilder и используйте EntityConnectionStringBuilder в качестве аргумента в конструкторе для контекста.
кэшировать строку подключения по имени пользователя. Простой пример использования нескольких общих методов для обработки добавления / извлечения из кэша.
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;
}
}
в моем случае я использую ObjectContext в отличие от DbContext, поэтому я изменил код в принятом ответе для этой цели.
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
}
}
}
Я хотел бы иметь несколько источников данных в конфигурации приложения. Поэтому после настройки раздела В приложении.config Я заменил источник данных, а затем передал его в dbcontext в качестве строки подключения.
//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;
Мне потребовалось некоторое время, чтобы понять. Надеюсь, это кому-то поможет. Я все слишком усложнял. до этого.
у меня есть два метода расширения для преобразования обычной строки подключения в формат Entity Framework. Эта версия хорошо работает с проектами библиотеки классов без копирования строк подключения из приложения.файл конфигурации для основного проекта. Это VB.Net но легко конвертировать в 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
после этого я создаю частичный класс для 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
создать запрос:
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());
попробуйте этот код...