【问题标题】:Dynamic MySQL database connection for Entity Framework 6Entity Framework 6 的动态 MySQL 数据库连接
【发布时间】:2013-12-15 04:09:21
【问题描述】:

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

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

【问题讨论】:

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


    【解决方案1】:

    Entity Framework 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>
    

    还可以在&lt;entityFramework /&gt;&lt;providers /&gt; 节点内添加提供程序,可选(在我的答案的第二部分中这是绝对必须的,在处理动态定义的数据库时)您可以更改&lt;defaultConnectionFactory /&gt; 节点:

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

    如果从默认的 sql server 连接中更改 defaultConnectionFactory,请不要忘记删除嵌套在 defaultConnectionFactory 节点中的 &lt;parameter&gt; 节点。 MysqlConnectionFactory 的构造函数不带任何参数,如果参数还在,就会失败。

    在这个阶段,用Entity连接MySQL是很容易的,你可以通过名字来引用上面的connectionString。请注意,如果按名称连接,即使 defaultConnectionFactory 节点仍指向 SQL Server(默认情况下),这也将起作用。

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

    只要正常连接即可:

    ApplicationDbContext db = ApplicationDbContext();
    

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

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

    重要提示

    如果您还没有,如果您希望连接到 MySQL,则必须更改 Web.config 中的 defaultConnectionFactory 动态的。因为我们将直接将连接字符串传递给 上下文构造函数,它不会知道要使用哪个提供者和 除非在 网络配置。有关如何执行此操作,请参见上文。

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

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

    希望这可以帮助某人:)

    【讨论】:

    • 您将连接字符串命名为“mysqlCon”,但在代码中您将“MyDatabase”作为参数传递。这是一个错误吗?
    • 没问题,你的回答对我也很有帮助。
    • 我只是想留下一些东西,而不仅仅是点赞。感谢您将其分解为最简单的核心部分!
    • 嗨@francisco.preller,您已将默认连接设置为mysql,但如果我们需要将providerName 设置为mssql/anything 动态,比?
    【解决方案2】:

    现在是 2019 年,当然事情发生了一些变化,但弗朗西索的例子确实帮助了我。这是我能找到的最简单的解决方案,也是唯一真正有效的解决方案。我确实从他所展示的内容中进行了一些更改。按照这个完成,你应该最终得到一个可行的解决方案。

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

    这是基于 Visual Studio 2015 Entityframework 6 使用 MySql 服务器 8.0.16.0 构建的。
    不幸的是,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 数据库的模板。

    第一步

    我为此在我的 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();
            }
    

    第三步

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

    <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 的任何位置。

    这实际上是类似于以下 3 个中的任何一个的行,它们分别连接到数据库 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 小时研究不同的解决方案,这些解决方案根本不起作用或提供足够的信息来完成它们。正如我所说,找到弗朗西索的例子让我能够让它发挥作用。

    问候,

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-24
      • 2017-05-28
      • 2021-10-02
      • 2013-12-16
      • 2023-04-09
      • 1970-01-01
      相关资源
      最近更新 更多