Mam projekt internetowego interfejsu API, który odwołuje się do mojego modelu i zestawów DAL. Użytkownikowi pojawia się ekran logowania, na którym może wybierać różne bazy danych.
Buduję parametry połączenia w następujący sposób:
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()
};
}
Przede wszystkim, jak właściwie zmienić połączenie kontekstu danych?
Po drugie, ponieważ jest to projekt internetowego interfejsu API, czy parametry połączenia (ustawione przy logowaniu według powyższego) są trwałe podczas interakcji użytkownika, czy też powinny być przekazywane za każdym razem do mojego kontekstu danych?
c#
entity-framework
asp.net-web-api
connection-string
Ivan-Mark Debono
źródło
źródło
Odpowiedzi:
Trochę za późno na tę odpowiedź, ale myślę, że istnieje potencjalny sposób na zrobienie tego za pomocą zgrabnej, małej metody rozszerzenia. Możemy skorzystać z konwencji EF zamiast konfiguracji oraz kilku małych wywołań struktury.
W każdym razie skomentowany kod i przykładowe użycie:
klasa metody rozszerzającej:
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 } } }
podstawowe zastosowanie:
// 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 );
Wiem, że masz już podstawową funkcjonalność, ale pomyślałem, że dodałoby to trochę różnorodności.
źródło
Controller
, który zawsze ustawi „db” kontrolera na bazę danych konkretnego klienta. To również uwalnia mnie (lub przyszłych administratorów / programistów) od konieczności tworzenia nowych parametrów połączenia dla każdego dodawanego klienta.// add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder { ProviderConnectionString = new SqlConnectionStringBuilder(System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString).ConnectionString };
DbContext
ma przeciążenie konstruktora, które akceptuje nazwę parametrów połączenia lub same parametry połączenia. Zaimplementuj własną wersję i przekaż ją do konstruktora podstawowego:public class MyDbContext : DbContext { public MyDbContext( string nameOrConnectionString ) : base( nameOrConnectionString ) { } }
Następnie po prostu przekaż nazwę skonfigurowanych parametrów połączenia lub same parametry połączenia podczas tworzenia wystąpienia
DbContext
var context = new MyDbContext( "..." );
źródło
Odpowiedź Jima Tollana działa świetnie, ale pojawił się błąd: słowo kluczowe nie jest obsługiwane jako „źródło danych”. Aby rozwiązać ten problem, musiałem zmienić tę część jego kodu:
// add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder (System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString);
do tego:
// add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder { ProviderConnectionString = new SqlConnectionStringBuilder(System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString).ConnectionString };
Bardzo przepraszam. Wiem, że nie powinienem odpowiadać na inne odpowiedzi, ale moja odpowiedź jest za długa na komentarz :(
źródło
Utworzona klasa jest „częściowa”!
public partial class Database1Entities1 : DbContext { public Database1Entities1() : base("name=Database1Entities1") { }
... i nazywasz to tak:
using (var ctx = new Database1Entities1()) { #if DEBUG ctx.Database.Log = Console.Write; #endif
więc musisz tylko utworzyć częściowy plik własnej klasy dla oryginalnej automatycznie wygenerowanej klasy (z tą samą nazwą klasy!) i dodać nowy konstruktor z parametrem połączenia, tak jak wcześniej odpowiedź Moho.
Po tym możesz użyć sparametryzowanego konstruktora przeciwko oryginalnemu. :-)
przykład:
using (var ctx = new Database1Entities1(myOwnConnectionString)) { #if DEBUG ctx.Database.Log = Console.Write; #endif
źródło
Dodaj wiele parametrów połączenia w pliku web.config lub app.config.
Następnie możesz uzyskać je jako ciąg, taki jak:
System.Configuration.ConfigurationManager. ConnectionStrings["entityFrameworkConnection"].ConnectionString;
Następnie użyj ciągu, aby ustawić:
Lepiej jest to wyjaśnione tutaj:
Odczytaj parametry połączenia z web.config
źródło
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);
Możesz pobrać parametry połączenia z web.config i po prostu ustawić je w konstruktorze EntityConnectionStringBuilder i użyć EntityConnectionStringBuilder jako argumentu w konstruktorze dla kontekstu.
Buforuj parametry połączenia według nazwy użytkownika. Prosty przykład wykorzystujący kilka ogólnych metod do obsługi dodawania / pobierania z pamięci podręcznej.
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; } }
źródło
W moim przypadku używam ObjectContext w przeciwieństwie do DbContext, więc w tym celu poprawiłem kod w akceptowanej odpowiedzi.
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 } } }
źródło
Chciałem mieć wiele źródeł danych w konfiguracji aplikacji. Dlatego po skonfigurowaniu sekcji w pliku app.config podmieniłem źródło danych, a następnie przekazałem je do kontekstu dbcontext jako ciąg połączenia.
//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;
Trochę mi to zajęło zrozumienie. Mam nadzieję, że to komuś pomoże. Zbyt skomplikowałem to. przed tym.
źródło
Mam dwie metody rozszerzające, aby przekonwertować normalne parametry połączenia na format Entity Framework. Ta wersja działa dobrze z projektami bibliotek klas bez kopiowania parametrów połączenia z pliku app.config do projektu głównego. To jest VB.Net, ale można go łatwo przekonwertować na 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
Następnie tworzę częściową klasę dla 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
Tworzenie zapytania:
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
źródło
W przypadku baz danych SQL Server i SQLite użyj:
_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);
źródło
Linq2SQLDataClassesDataContext db = new Linq2SQLDataClassesDataContext(); var query = from p in db.SyncAudits orderby p.SyncTime descending select p; Console.WriteLine(query.ToString());
wypróbuj ten kod ...
źródło