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") { }
... и вы называете это так:
using (var ctx = new Database1Entities1()) { #if DEBUG ctx.Database.Log = Console.Write; #endif
Итак, вам нужно создать только частично собственные файл класса для оригинальные автоматически сгенерированного класса (с тем же именем класса!) и добавить новый конструктор с параметром строки подключения, как ответ Мохо раньше.
после этого вы можете использовать параметризованный конструктор против оригинала. : -)
пример:
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);
Вы можете получить строку подключения из интернета.просто установите это в конструкторе 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