EntityFrameworkは実行時に接続を変更します


80

モデルとDALアセンブリを参照するWebAPIプロジェクトがあります。ユーザーにはログイン画面が表示され、そこでさまざまなデータベースを選択できます。

次のように接続文字列を作成します。

    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プロジェクトであるため、接続文字列(上記のログイン時に設定)はユーザーの操作全体を通じて永続的ですか、それとも毎回データコンテキストに渡す必要がありますか?


それがあなたの考え方/ツールボックスの要件に適合する場合に備えて、私は少し代替案を追加しました。
jim tollan 2013年

@ Ivan-Markこの部分をどのように解決しましたか?次に、これはWeb APIプロジェクトであるため、接続文字列(上記のログイン時に設定)はユーザーの操作全体を通じて永続的であるか、データコンテキストに毎回渡される必要があります
Narendra Singh Rathore

@NarendraSinghRathore接続文字列は、データベース名(または他の何か)をキーとして構成ファイルに保存されます。ユーザーはログイン時にデータベースを選択し、キーがユーザー名である可能性のあるキャッシュに保存されます。ユーザーが自分のユーザー名をヘッダーとして渡してリクエストを行うと、接続文字列が取得されてデータコンテキストに渡されます。
Ivan-Mark Debono 2017

@ Ivan-MarkDebonoこのキャッシュについて説明できますか?バックエンドでメモリキャッシュまたはセッションを使用していますか、それともフロントエンドでCookieとして保存していますか。ありがとう!
Narendra Singh Rathore 2017

1
シングルトンの@NarendraSinghRathoreMemoryCache
Ivan-Mark

回答:


110

この答えには少し遅れていますが、きちんとした小さな拡張方法でこれを行う潜在的な方法があると思います。設定より規約といくつかの小さなフレームワーク呼び出しを利用できます。

とにかく、コメントされたコードと使用例:

拡張メソッドクラス:

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

基本的な機能はすでに整っていると思いますが、これにより少し多様性が増すと思いました。


6
これは素晴らしいです、ありがとう!これControllerは、コントローラーの「db」を常に顧客固有のdbに設定する拡張機能とともに、マルチテナントプロジェクトで使用できます。これにより、私(または将来の管理者/開発者)は、追加されるすべてのクライアントに対して新しい接続文字列を作成する必要がなくなります。
LukeP 2014年

3
ええ、私は文字通り何日もこの問題に対する実行可能な堅牢な解決策を考え出すのに苦労しました、そしてこの単純な拡張方法は私の問題に答えました。去年の11月に作成して以来、変更を加える必要はなかったので、そのままロードテストされていると思います:)。とにかく、それがいくつかのボックスをチェックしてくれてうれしいです...話して良かったです。
jim tollan 2014年

5
このエラーが発生しますSystem.ArgumentException:キーワードはサポートされていません:EF4の「データソース」
sheshadri 2015

2
@ user1234エラー:キーワードはサポートされていません 'データソース'もあります。この問題を解決するために、私は彼のコードのこの部分を変更する必要がありました: // add a reference to System.Configuration var entityCnxStringBuilder = new EntityConnectionStringBuilder { ProviderConnectionString = new SqlConnectionStringBuilder(System.Configuration.ConfigurationManager .ConnectionStrings[configNameEf].ConnectionString).ConnectionString };
A.Ima 2016

2
@jimtollan新しいインスタンスを作成するたびに、app.configに保存されている古い接続文字列から作成されます。
Abdulsalam Elsharif 2018年

62

DbContext接続文字列の名前または接続文字列自体を受け入れるコンストラクターのオーバーロードがあります。独自のバージョンを実装し、それを基本コンストラクターに渡します。

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

次に、インスタンス化するときに、構成された接続文字列の名前または接続文字列自体を渡すだけです。 DbContext

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

DbContext派生クラスに関数がすでに存在することに気づかなかったので、それを使用しました。
ブライアンリーミング2016年

2
この回答は承認済みの回答としてマークする必要があると思います。
2017

2
この答えは素晴らしいですが、@ eMeLが説明しているように。このクラスは自動生成されるため、代わりに、このクラスに基づいて別のクラスを作成し、モデルを更新しても上書きされないようにする必要があります。
フアンカルロスオロペザ

4
@JuanCarlosOropeza:EFは、生成されたクラス(ボットコンテキストとエンティティ)を部分として巧妙にマークするため、独自のファイルを作成し、その中でDbContextを(部分として)再宣言し、そこにカスタム関数を追加できます。
dotNET 2018年

14

Jim Tollanの答えはうまく機能しますが、エラーが発生しました:キーワードはサポートされていません 'データソース'。この問題を解決するために、私は彼のコードのこの部分を変更しなければなりませんでした:

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

本当にごめんなさい。私は他の答えに答えるために答えを使うべきではないことを知っています、しかし私の答えはコメントには長すぎます:(


6

作成されたクラスは「部分的」です!

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

...そしてあなたはそれをこのように呼びます:

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

上記の解決策は私のために働いています。リンク
Kartik Goyal 2016年

0

web.configまたはapp.configに複数の接続文字列を追加します。

次に、それらを次のような文字列として取得できます。

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

次に、文字列を使用して設定します。

Provider
Metadata
ProviderConnectionString

ここでよりよく説明されています:

web.configから接続文字列を読み取ります


接続文字列は別のSQLサーバーデータベースに保存され、リストがユーザーに表示されます。
Ivan-Mark Debono 2013年

0
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を引数として使用できます。

ユーザー名で接続文字列をキャッシュします。キャッシュへの追加/キャッシュからの取得を処理するためにいくつかの一般的なメソッドを使用する簡単な例。

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

はい。ただし、ユーザーがコントローラーのアクションを呼び出すたびに、新しい接続文字列をdbcontextに渡す必要がありますか?
Ivan-Mark Debono 2013年

おそらく、各呼び出しの後にコンテキストを破棄するので、そうです。コンテキストは、1つの要求(作業単位)に対してのみ存在する必要があります。説明
scheien 2013年

では、セッション中、ユーザーの接続文字列をどこにどのように保存しますか?(多くのユーザーはWeb APIプロジェクトに接続でき、異なる接続
文字列を

キャッシュして、ユーザー名やその他のキーで取得するのはどうですか。
scheien 2013年

0

私の場合、DbContextではなくObjectContextを使用しているので、その目的のために受け入れられた回答のコードを微調整しました。

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

このエラーが発生しましたキーワードはサポートされていません: 'データソース'。私はEF4を使用しています
sheshadri 2015

0

アプリの構成に複数のデータソースが必要でした。したがって、app.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;

これは私が理解するのに少し時間がかかりました。私はそれが誰かを助けることを願っています。私はそれをあまりにも複雑にしていました。この前に。


0

通常の接続文字列をEntityFramework形式に変換するための2つの拡張方法があります。このバージョンは、app.configファイルからプライマリプロジェクトに接続文字列をコピーしなくても、クラスライブラリプロジェクトで適切に機能します。これは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

0

SQL ServerデータベースとSQLiteデータベースの両方で、次を使用します。

_sqlServerDBsContext = new SqlServerDBsContext(new DbContextOptionsBuilder<SqlServerDBsContext>().UseSqlServer("Connection String to SQL DB").Options);

SQLiteの場合、Microsoft.EntityFrameworkCore.Sqliteインストールされていることを確認してください。接続文字列は単に「 'DataSource =' +ファイル名」です。

_sqliteDBsContext = new SqliteDBsContext(new DbContextOptionsBuilder<SqliteDBsContext>().UseSqlite("Connection String to SQLite DB").Options);

-6
Linq2SQLDataClassesDataContext db = new Linq2SQLDataClassesDataContext();

var query = from p in db.SyncAudits orderby p.SyncTime descending select p;
Console.WriteLine(query.ToString());

このコードを試してください...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.