Entity Framework 6的动态MySQL数据库连接

时间:2013-11-29 02:58:09

标签: c# mysql asp.net entity-framework entity-framework-6

我希望将动态连接字符串传递给实体框架上下文。我有超过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,并且由于连接字符串无效而失败。

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

2 个答案:

答案 0 :(得分:54)

实体框架6提供了一些方便的细微更改,有助于使MySQL工作以及创建动态数据库连接。

让MySQL使用Entity Framework 6

首先,在我回答这个问题之日,与EF6兼容的唯一.Net连接器驱动程序是MySQL .Net Connectior 6.8.1(Beta开发版),可以找到at the official MySQL website here

安装后,从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 />节点内添加提供程序(在处理动态定义的数据库时,这是我答案的第二部分中绝对必须的),您可以更改{ {1}}节点:

<defaultConnectionFactory />

如果从默认的sql server连接更改defaultConnectionFactory,请不要忘记删除嵌套在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> 个节点。 MysqlConnectionFactory不为其构造函数接受任何参数,如果参数仍然存在,则会失败。

在这个阶段,使用Entity连接到MySQL非常容易,您只需按名称引用上面的connectionString即可。请注意,如果按名称连接,即使<parameter>节点仍指向SQL Server(默认情况下也是如此),这也会起作用。

defaultConnectionFactory

这只是正常连接的问题:

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

连接到动态选择的数据库名称

此时很容易连接到我们可以作为参数传递的数据库,但我们需要做一些事情。

重要提示

  

如果您还没有,如果您希望连接到MySQL,则必须更改Web.config中的defaultConnectionFactory   动态。因为我们将直接传递连接字符串   在上下文构造函数中,它不知道要使用哪个提供程序   将转为默认连接工厂,除非指定   web.config中。请参阅上文,了解如何执行此操作。

您可以手动将连接字符串传递给上下文:

ApplicationDbContext db = ApplicationDbContext();

但为了使它更容易一些,我们可以在设置mySQL时对上面建立的连接字符串进行一些小改动。只需添加一个占位符,如下所示:

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

现在我们可以构建一个帮助器方法并更改ApplicationDbContext类,如下所示:

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

如果您正在使用数据库迁移,则以下步骤非常重要

如果您正在使用迁移,您会发​​现ApplicationDbContext将由框架传递给您的Seed方法,它将失败,因为它不会传入我们为数据库名称输入的参数。

将以下类添加到上下文类的底部(或任何地方)以解决该问题。

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

您的代码优先迁移和种子方法现在将以MySQL数据库中的public class MigrationsContextFactory : IDbContextFactory<ApplicationDbContext> { public ApplicationDbContext Create() { return new ApplicationDbContext("developmentdb"); } } 架构为目标。

希望这有助于某人:)

答案 1 :(得分:0)

当然,现在是2019年,情况有所变化,但是Franciso的榜样确实帮助了我。这是我能找到的最简单的解决方案,也是唯一实际可行的解决方案。我确实从他的显示中进行了一些更改。按照此步骤完成操作,您应该最终得到可行的解决方案。

我不得不改变一些事情。我将非常明确地说明必须执行的操作,并且我将使用实际的文件名等,这样您就不必猜测替换了。最后还缺少许多示例来说明如何使它工作。此示例包含您需要了解的所有内容。

这是使用MySql服务器8.0.16.0在visual studio 2015 Entityframework 6上构建的。
不幸的是,MySql连接器和库是一团糟。 8.0.xx.0连接器/ net和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的,但是我真的不知道为什么它不能用于任何数据库。

场景

我有一个多租户情况,我有一个公共数据库和多个租户数据库,每个客户一个。客户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" />

第二步

我使用向导创建了一个名为ClientDbUserUpdater的新实体。数据实体称为

ClientDbUserUpdater.edmx

我告诉它使用“ DefaultClientConnection”作为数据库连接 我告诉它将此新的连接字符串保存在Web.config中

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

Step3

此新连接字符串是您需要更改的字符串。我尚未进行测试,但是我很确定如果使用向导更改数据实体模型,则需要再次进行此更改。 取字符串:

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

 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 = "someone@someplace.com"
     _user.FirstName ' "Someone"; 
     _user.LastName  = "New";
     _user.Active = true;

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

希望这对某些人有帮助。我花了大约20个小时来研究不同的解决方案,这些解决方案根本行不通或无法提供足够的信息来完成它们。正如我所说,找到弗朗索索的榜样可以使我成功。

此致