it-swarm.com.de

Verbindung zum Entity Framework zur Laufzeit ändern

Ich habe ein Web-API-Projekt, das auf meine Modell- und DAL-Baugruppen verweist. Dem Benutzer wird ein Anmeldebildschirm angezeigt, auf dem er verschiedene Datenbanken auswählen kann.

Ich baue die Verbindungszeichenfolge wie folgt:

    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()
        };
    }

Erstens, wie kann ich die Verbindung des Datenkontexts tatsächlich ändern?

Und zweitens, da es sich um ein Web-API-Projekt handelt, ist die Verbindungszeichenfolge (bei Login oben festgelegt) während der gesamten Benutzerinteraktion dauerhaft oder sollte sie jedes Mal in meinen Datenkontext übergeben werden?

68

Zu dieser Antwort etwas spät, aber ich denke, es gibt einen möglichen Weg, dies mit einer ordentlichen kleinen Erweiterungsmethode zu tun. Wir können die EF-Konvention gegenüber der Konfiguration und ein paar kleine Rahmenaufrufe nutzen.

Wie auch immer, der kommentierte Code und die Verwendung von Beispielen:

erweiterungsmethodenklasse:

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
        }
    }
}

grundnutzung:

// 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
    );

Ich weiß, dass Sie bereits über die Grundfunktionalität verfügen, aber dies würde ein wenig Abwechslung bewirken.

100
jim tollan

DbContext verfügt über eine Konstruktorüberladung, die den Namen einer Verbindungszeichenfolge oder einer Verbindungszeichenfolge selbst akzeptiert. Implementieren Sie Ihre eigene Version und übergeben Sie diese an den Basiskonstruktor:

public class MyDbContext : DbContext
{
    public MyDbContext( string nameOrConnectionString ) 
        : base( nameOrConnectionString )
    {
    }
}

Übergeben Sie einfach den Namen einer konfigurierten Verbindungszeichenfolge oder eine Verbindungszeichenfolge selbst, wenn Sie Ihre DbContext-Instanz instanziieren.

var context = new MyDbContext( "..." );
54
Moho

Die Antwort von Jim Tollan funktioniert großartig, aber ich habe das Error: Schlüsselwort "Datenquelle" nicht unterstützt. Um dieses Problem zu lösen, musste ich diesen Teil seines Codes ändern:

// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
    (System.Configuration.ConfigurationManager
            .ConnectionStrings[configNameEf].ConnectionString);

zu diesem:

// add a reference to System.Configuration
var entityCnxStringBuilder = new EntityConnectionStringBuilder
{
    ProviderConnectionString = new  SqlConnectionStringBuilder(System.Configuration.ConfigurationManager
               .ConnectionStrings[configNameEf].ConnectionString).ConnectionString
};

Es tut mir wirklich leid. Ich weiß, dass ich keine Antworten verwenden sollte, um auf andere Antworten zu antworten, aber meine Antwort ist zu lang für einen Kommentar :(

11
A.Ima

Die erstellte Klasse ist "partiell"!

public partial class Database1Entities1 : DbContext
{
    public Database1Entities1()
        : base("name=Database1Entities1")
    {
    }

... und Sie nennen es so:

using (var ctx = new Database1Entities1())
      {
        #if DEBUG
        ctx.Database.Log = Console.Write;
        #endif

sie müssen also nur eine partielle own class-Datei für die ursprünglich automatisch generierte Klasse erstellen (mit demselben Klassennamen!) und einen neuen Konstruktor mit Verbindungszeichenfolge-Parameter hinzufügen, wie zuvor Mohos Antwort.

Danach können Sie den parametrisierten Konstruktor für das Original verwenden. :-)

beispiel:

using (var ctx = new Database1Entities1(myOwnConnectionString))
      {
        #if DEBUG
        ctx.Database.Log = Console.Write;
        #endif
6
eMeL

Fügen Sie mehrere Verbindungszeichenfolgen in Ihrer web.config oder app.config hinzu.

Dann können Sie sie als Zeichenfolge erhalten:

System.Configuration.ConfigurationManager.
    ConnectionStrings["entityFrameworkConnection"].ConnectionString;

Verwenden Sie dann die Zeichenfolge, um Folgendes festzulegen:

Provider
Metadata
ProviderConnectionString

Es ist besser hier erklärt:

Liest die Verbindungszeichenfolge aus der web.config

0
Bryan Arbelo

In meinem Fall verwende ich ObjectContext im Gegensatz zu DbContext, daher habe ich den Code in der akzeptierten Antwort für diesen Zweck angepasst.

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
        }
    }
}
0
David

Ich wollte mehrere Datenquellen in der App-Konfiguration haben. Nachdem ich einen Abschnitt in der app.config eingerichtet hatte, tauschte ich die Datenquelle aus und übergab sie als Verbindungsstring an 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;

Das hat mich ein bisschen geklärt. Ich hoffe es hilft jemandem. Ich habe es viel zu kompliziert gemacht. vor dem. 

0
Jake Porter
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);

Sie können die Verbindungszeichenfolge aus der Datei web.config abrufen. Legen Sie diese im EntityConnectionStringBuilder-Konstruktor fest und verwenden Sie den EntityConnectionStringBuilder als Argument im Konstruktor für den Kontext.

Zwischenspeichern Sie die Verbindungszeichenfolge nach Benutzername. Ein einfaches Beispiel, bei dem einige generische Methoden zum Hinzufügen/Abrufen aus dem Cache verwendet werden. 

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;
        }
    }
0
scheien

Ich habe zwei Erweiterungsmethoden zum Konvertieren der normalen Verbindungszeichenfolge in das Entity Framework-Format. Diese Version funktioniert gut mit Klassenbibliothekprojekten, ohne die Verbindungszeichenfolgen aus der Datei app.config in das Primärprojekt zu kopieren. Dies ist VB.Net, aber leicht in C # zu konvertieren.

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

Danach erstelle ich eine Teilklasse für 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

Abfrage erstellen:

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
0
SZL