C# 实体框架6的动态MySQL数据库连接

C# 实体框架6的动态MySQL数据库连接,c#,mysql,asp.net,entity-framework,entity-framework-6,C#,Mysql,Asp.net,Entity Framework,Entity Framework 6,我希望向实体框架上下文传递一个动态连接字符串。我有150多个相同的模式(每个帐户一个),我希望选择这样的连接: ApplicationDbContext db = new ApplicationDbContext("dbName"); 理论上这相当容易,因为我可以创建一个connectionString并将其作为构造函数的参数传递,例如: public ApplicationDbContext(string dbName) : base(GetConnectionString(dbName))

我希望向实体框架上下文传递一个动态连接字符串。我有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”
属性

但是,当我将实际的连接字符串动态传递给连接时,它假定它需要连接到SQL Server而不是MySQL,并且由于连接字符串无效而失败


问题是,如果动态创建连接字符串,如何将提供程序名称传递给该字符串?

Entity Framework 6提供了一些方便的细微更改,这些更改有助于MySQL正常工作和创建动态数据库连接

让MySQL使用实体框架6 首先,在我回答这个问题时,唯一与EF6兼容的.Net连接器驱动程序是MySQL.Net connector 6.8.1(Beta开发版本),可以找到它

安装后,请从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>
这只是一个正常连接的问题:

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" />
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");
    }
}
公共类迁移ContextFactory:IDbContextFactory
{
公共应用程序上下文创建()
{
返回新的ApplicationDbContext(“developmentdb”);
}
}
代码优先迁移和种子方法现在将以MySQL数据库中的
developmentdb
模式为目标


希望这对某人有所帮助:)

现在是2019年,事情当然有了一些变化,但弗朗西索的例子确实在这方面帮助了我。这是我能找到的最简单的解决方案,也是唯一有效的解决方案。我确实对他所展示的有所改变。按照这个步骤完成,你应该会得到一个有效的解决方案

我不得不改变一些事情。我将非常明确地说明必须做什么,我将使用我的实际文件名等,这样您就不必猜测替换。许多例子也缺少如何使其在最后发挥作用。这个例子有你需要知道的一切

这是在visual studio 2015 Entityframework 6上使用MySql服务器8.0.16.0构建的。
不幸的是,MySql连接器和库完全是一团糟。8.0.xx.0 connector/net和MySql.Data.Entity.EF6以及MySql.Data完全没有用处。 我已经安装了ConnectorNet6.10.7.0、MySql.Data.Entity.EF6.10.7.0和MySql.Data 6.10.7.0。这对我有效,我将坚决反对改变这一点。

这是针对MySql的,但我真的不知道为什么它不能用于任何db

场景

我有一个多租户的情况,我有一个公共数据库和多个tentant数据库,每个客户一个客户id保存在公共数据库中,用于登录和授权,客户id指示使用哪个数据库。客户机数据库都称为myclientdb_x,其中x是客户机编号。myclientdb_1、myclientdb_2、myclientdb_35等等

我需要动态切换到代码当前服务的任何clientdb_x。有一个名为myclient_0的初始数据库客户端,它是所有其他myclient_x数据库的模板

步骤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" />
res:string是数据实体所需要的,这就是为什么不能将标准连接字符串发送到数据实体中的原因

如果您尝试发送初始连接字符串

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

这个新的连接字符串就是您需要更改的字符串。我还没有对它进行测试,但我非常确定,如果使用向导更改数据实体模型,您将需要再次进行此更改。 以字符串为例:

<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" />
此代码可以位于解决方案中任何可以使用的位置
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.csdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.ssdl|
    res://*/Areas.Authorizations.Models.ClientDbUserUpdater.msl;
 <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();
        }
<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" />
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" />
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_0Entities = new client_0Entities("client_19");
 client_0Entities _client_0Entities = new client_0Entities("client_47");
 client_0Entities _client_0Entities = new client_0Entities("client_68");
  string _newSchema = "client_19"
  using(client_0Entities _client_0Entities = new client_0Entities(_newSchema))
  {
     user _user = new user();
     _user.UserId = 201;
     _user.Email = "someone@someplace.com"
     _user.FirstName ' "Someone"; 
     _user.LastName  = "New";
     _user.Active = true;

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