web-dev-qa-db-ja.com

Entity Framework 6の動的MySQLデータベース接続

動的な接続文字列をエンティティフレームワークコンテキストに渡したい。同一の(アカウントごとに1つ)150を超えるスキーマがあり、そのような接続を選択したいと思います。

ApplicationDbContext db = new ApplicationDbContext("dbName");

ConnectionStringを作成し、コンストラクタの引数として渡すことができるため、理論的にはこれはかなり簡単です。たとえば、

public ApplicationDbContext(string dbName) : base(GetConnectionString(dbName))
{
}

public static string GetConnectionString(string dbName)
{
    // The connectionString passed is something like:
    // Server=localhost;Database={0};Uid=username;Pwd=password
    var connString =  ConfigurationManager
                         .ConnectionStrings["MyDatabase"]
                         .ConnectionString
                         .ToString();

    return String.Format(connString, dbName);
}

接続文字列名を渡すだけで正常に接続できますが、以下のように動的に生成するときは接続できません。 web.configの接続文字列にproviderName="MySql.Data.MySqlClient"属性。

ただし、実際の接続文字列を接続に動的に渡す場合、MySQLではなくSQL Serverに接続する必要があると想定し、接続文字列が無効であるために失敗します。

問題は、接続文字列を動的に作成する場合、接続文字列にプロバイダー名をどのように渡すかです。

22

Entity Framework 6は、MySQLの動作と動的なデータベース接続の作成の両方を支援する便利な微妙な変更を提供します。

MySQLをEntity Framework 6で動作させる

まず、この質問に答えた日に、EF6と互換性のある.Netコネクタドライバーは、MySQL .Net Connectior 6.8.1(ベータ開発バージョン)のみです 公式のMySQL Webサイト =。

インストール後、Visual Studioソリューションから次のファイルを参照します。

  • Mysql.Data.dll
  • Mysql.Data.Entity.EF6.dll

また、これらのファイルは、binディレクトリなど、ビルド時にプロジェクトがアクセスできる場所にコピーする必要があります。

次に、Web.config(またはデスクトップベースの場合はApp.config)ファイルにいくつかのアイテムを追加する必要があります。

接続文字列:

<connectionStrings>
    <add name="mysqlCon"
         connectionString="Server=localhost;Database=dbName;Uid=username;Pwd=password" 
         providerName="MySql.Data.MySqlClient" />
</connectionStrings>

また、オプションで<entityFramework />および<providers />ノード内にプロバイダーを追加します(動的に定義されたデータベースを処理する場合、これは私の答えの2番目の部分で絶対必要です)<defaultConnectionFactory />ノードを変更できます。

<entityFramework>
    <defaultConnectionFactory type="MySql.Data.Entity.MySqlConnectionFactory, MySql.Data.Entity.EF6" />
    <providers>
        <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6" />
    </providers>
</entityFramework>

defaultConnectionFactoryをデフォルトのSQLサーバー接続から変更する場合、defaultConnectionFactoryノードにネストされている<parameter>ノードを削除することを忘れないでください。 MysqlConnectionFactoryはコンストラクターのパラメーターを受け取らず、パラメーターがまだある場合は失敗します。

この段階では、Entityを使用してMySQLに接続するのは非常に簡単です。名前で上のconnectionStringを参照するだけです。名前で接続する場合、defaultConnectionFactoryノードがまだSQL Serverを指している場合でも機能することに注意してください(デフォルトで接続します)。

public class ApplicationDbContext: DbContext
{
    public ApplicationDbContext() : base("mysqlCon")
    {
    }
}

それは単に通常の接続の問題です:

ApplicationDbContext db = ApplicationDbContext();

動的に選択されたデータベース名への接続

この時点で、パラメータとして渡すことができるデータベースに接続するのは簡単ですが、やらなければならないことがいくつかあります。

重要な注意点

MySQLに動的に接続したい場合は、Web.configのdefaultConnectionFactoryをまだ変更していない必要があります。接続文字列をコンテキストコンストラクターに直接渡すため、web.configで指定されていない限り、使用するプロバイダーがわからず、既定の接続ファクトリーになります。上記の方法を参照してください。

次のように、コンテキストに接続文字列を手動で渡すことができます。

public ApplicationDbContext() : base("Server:localhost;...")
{
}

しかし、それを少し簡単にするために、mySQLをセットアップするときに上で行った接続文字列に小さな変更を加えることができます。以下に示すように、プレースホルダーを追加するだけです。

<add name="mysqlCon" connectionString="Server=localhost;Database={0};Uid=username;Pwd=password" providerName="MySql.Data.MySqlClient" />

これで、ヘルパーメソッドを構築し、以下に示すようにApplicationDbContextクラスを変更できます。

public class ApplicationDbContext: DbContext
{
    public ApplicationDbContext(string dbName) : base(GetConnectionString(dbName))
    {
    }

    public static string GetConnectionString(string dbName)
    {
        // Server=localhost;Database={0};Uid=username;Pwd=password
        var connString = 
            ConfigurationManager.ConnectionStrings["mysqlCon"].ConnectionString.ToString();

        return String.Format(connString, dbName);
    }
}

データベースの移行を使用している場合、次の手順が重要です

移行を使用している場合、フレームワークによってApplicationDbContextがSeedメソッドに渡され、データベースに入れたパラメーターを渡さないため失敗します。名前。

この問題を解決するには、コンテキストクラスの下部(または実際にどこかに)に次のクラスを追加します。

public class MigrationsContextFactory : IDbContextFactory<ApplicationDbContext>
{
    public ApplicationDbContext Create()
    {
        return new ApplicationDbContext("developmentdb");
    }
}

これで、コードファーストの移行とシードメソッドは、MySQLデータベースのdevelopmentdbスキーマをターゲットにします。

これが誰かを助けることを願っています:)

54

もちろん2019年になりましたが、状況は少し変わっていますが、Francisoの例は本当に私を助けてくれました。これは私が見つけることができる最も簡単な解決策であり、実際に機能した唯一の解決策です。彼が示したものから少し変更しました。これに従って作業を完了してください。

いくつか変更しなければなりませんでした。私は何をしなければならないかを非常に明確にし、実際のファイル名などを使用して、置換について推測する必要がないようにします。多くの例は、最後にそれを機能させる方法についても短いです。この例には、知っておく必要があるすべてのものが含まれています。

これはVisual Studio 2015 Entityframework 6でMySqlサーバー8.0.16.0を使用して構築されました。
残念ながら、MySqlコネクタとライブラリは完全に混乱しています。 8.0.xx.0コネクタ/ネットとMySql.Data.Entity.EF6およびMySql.Dataはまったく役に立ちません。 Connector Net 6.10.7.0、MySql.Data.Entity.EF6 6.10.7.0、およびMySql.Data 6.10.7.0をインストールしました。それは私のために働いており、私はこれを変更することに激しく反対します。

これはMySql向けですが、どのdbでも機能しなかった理由は本当にわかりません。

シナリオ

顧客ごとに1つの共通dbと複数のテナントデータベースがあるマルチテナントの状況があります。顧客IDは、ログインと認証のために共通dbに保持され、顧客IDは使用するデータベースを指示します。クライアントデータベースはすべてmyclientdb_xと呼ばれ、xはクライアント番号です。 myclientdb_1、myclientdb_2、myclientdb_35など。

現在コードが提供しているclientdb_xに動的に切り替える必要があります。他のすべてのmyclient_xデータベースのテンプレートであるmyclient_0と呼ばれる初期データベースクライアントがあります。

ステップ1

このために、Web.configで特定の接続文字列を作成しました。 clientdb_0への接続を許可します

<add name="DefaultClientConnection" providerName="MySql.Data.MySqlClient" 
    connectionString="server=localhost;user id=xxx;
     password=xxxx; persistsecurityinfo=True;database=clientdb_0" />

ステップ2

ウィザードを使用して、ClientDbUserUpdaterという新しいエンティティを作成しました。データエンティティが呼び出されます

ClientDbUserUpdater.edmx

この新しい接続文字列をWeb.configに保存するように指示したDB接続として「DefaultClientConnection」を使用するように指示しました

これにより、Web.configファイルに新しいエンティティ接続文字列が作成され、次のようになります。

<add name="myclient_0Entities" connectionString="metadata=
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
     provider=MySql.Data.MySqlClient;provider connection string=&quot;
      server=localhost;user id=xxxx;password=yyyyy;
     persistsecurityinfo=True;database=myclient_0&quot;" providerName="System.Data.EntityClient" />

ウィザードは\ nを適切な場所に配置するのが適切でないため、少し掘り下げなければならない場合があります。

この接続文字列は、名前とその名前を除いて、基本的に初期接続文字列と同じであることに注意してください

    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;

Res:文字列はデータエンティティに必要であり、そのため、標準の接続文字列をデータエンティティに送信できないだけです。

最初の接続文字列で送信しようとした場合

 <add name="DefaultClientConnection" providerName="MySql.Data.MySqlClient" 
        connectionString="server=localhost;user id=xxx;
         password=xxxx; persistsecurityinfo=True;database=clientdb_0" />

あなたはから例外を取得します

      protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }

Step

この新しい接続文字列は、変更する必要があるものです。私はそれをテストしていませんが、ウィザードでデータエンティティモデルを変更する場合、この変更を再度行う必要があると確信しています。文字列を取る:

<add name="myclient_0Entities" connectionString="metadata=
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
     provider=MySql.Data.MySqlClient;provider connection string=&quot;
      server=localhost;user id=xxxx;password=yyyyy;
     persistsecurityinfo=True;database=myclient_0&quot;" providerName="System.Data.EntityClient" />

それを次のように変更します。

<add name="myclient_0Entities" connectionString="metadata=
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
     provider=MySql.Data.MySqlClient;provider connection string=&quot;
      server=localhost;user id=xxxx;password=yyyyy;
     persistsecurityinfo=True;database={0}&quot;" providerName="System.Data.EntityClient" />

変更された部分は、database = myclient_0からdatabase = {0}のみであることに注意してください。

ステップ4

データエンティティは、コードビハインドClientDbUserUpdater.edmxを作成しました。ファイルの名前はClientDbUserUpdater.Context.csです。

コードは...

namespace what.ever.your.namespace.is
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;

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

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }

        public virtual DbSet<user> users { get; set; }
    }
}

これは部分クラスであることに注意してください。つまり、このクラスを拡張し、新しいコンストラクターを追加できます。

次のクラスを追加します。

using System;
using System.Configuration ;
using System.Data.Entity ;

namespace what.ever.your.namespace.is
{  
  public partial class client_0Entities : DbContext
  {
    public client_0Entities(string dbName) : base(GetConnectionString(dbName))
    {
    }

    public static string GetConnectionString(string dbName)
    {       
       var connString = ConfigurationManager.ConnectionStrings["client_0Entities"].ConnectionString.ToString();
      // obviously the next 2 lines could be done as one but creating and 
      // filling a string is better for debugging.  You can see what happened 
      // by looking a  conn
      // return  String.Format(connString, dbName);
      string conn =  String.Format(connString, dbName);
      return conn ;
    }
  } 
}

このクラスは、上記のようなデータエンティティモデルの基本接続文字列を取得できる新しいコンストラクターを追加します。

<add name="myclient_0Entities" connectionString="metadata=
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
     provider=MySql.Data.MySqlClient;provider connection string=&quot;
      server=localhost;user id=xxxx;password=yyyyy;
     persistsecurityinfo=True;database={0}&quot;" providerName="System.Data.EntityClient" />

スキーマを変更するには、実行時に変更します。

新しい部分クラスのString.Format()呼び出しは、実行時にこの接続文字列のデータベーススキーマ名をスワップアウトします。

この時点で、すべての構成が完了しました。

ステップ5

今、あなたはそれを行くことができます。この例をよりよく理解するために、このエンティティのモデルがどのように見えるかを知っておくと便利です。私はただテストして、それを成功させようとしていたので、それは非常に簡単です。

ClientDbUserUpdater.edmxからClientDbUserUpdater.ttにドリルダウンすると、modelname.csにモデルが見つかります。私のモデルは「ユーザー」と呼ばれるため、ファイル名はuser.csと呼ばれます

namespace what.ever.your.namespace.is
{
    using System;
    using System.Collections.Generic;

    public partial class user
    {
        public int UserId { get; set; }
        public string Email { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Nullable<bool> Active { get; set; }
    }
}

これで、一般的にこのようにモデルにアクセスできます。

 client_0Entities _client_0Entities = new client_0Entities("schemaName");

このコードは、クラスclient_0Entitiesを見ることができるソリューション内の任意の場所に配置できます。

これは、実際には、それぞれデータベースclient_19、client_47およびclient_68への接続である以下の3つのいずれかに似た行です。

 client_0Entities _client_0Entities = new client_0Entities("client_19");
 client_0Entities _client_0Entities = new client_0Entities("client_47");
 client_0Entities _client_0Entities = new client_0Entities("client_68");

以下は、私のシステムで動作する実際のコード例です。明らかに、「client_19」でハードコーディングするつもりはありませんが、デモの目的には向いています。

これは、実際の名前を持つ実際のコードで、データベースclient_19のユーザーテーブルに機能し、新しい行を追加します

  string _newSchema = "client_19"
  using(client_0Entities _client_0Entities = new client_0Entities(_newSchema))
  {
     user _user = new user();
     _user.UserId = 201;
     _user.Email = "[email protected]"
     _user.FirstName ' "Someone"; 
     _user.LastName  = "New";
     _user.Active = true;

     client_0Entities.users.Add ( _user ) ;
     client_0Entities.SaveChangesAsync ( ) ;
  }

うまくいけば、これが一部の人々を助ける。私は約20時間を費やしてさまざまなソリューションを調べましたが、それらは単に機能しないか、それらを完了するのに十分な情報を提供しませんでした。先ほど言ったように、Francisoの例を見つけることで、動作させることができました。

よろしく、

0
BrownPony