7.1 实体框架 6 支持

MySQL Connector/NET 集成了对 Entity Framework 6 (EF6) 的支持,现在包括对 EF 6.4 版本的跨平台应用程序部署的支持。本章介绍如何配置和使用在 Connector/NET 中实现的 EF6 功能。

在这个部分:

仅适用于 Windows 的 EF6 的最低要求

  • 连接器/NET 6.10 或 8.0.11

  • MySQL 服务器 5.6

  • 实体框架 6 程序集

  • .NET Framework 4.5.1(用于连接器/NET 8.0.22 的 .NET Framework 4.5.2)

具有跨平台支持的 EF 6.4 的最低要求

  • 连接器/NET 8.0.22

  • MySQL 服务器 5.6

  • 实体框架 6.4 程序集

  • .NET Standard 2.1(.NET Core SDK 3.1 和 Visual Studio 2019 版本 16.5)

配置

笔记

MySQL Connector/NET 8.0 版本系列的 EF6 程序集和 NuGet 包的命名方案不同于之前版本系列(例如 6.9 和 6.10)使用的方案。要配置 Connector/NET 6.9 或 6.10 以与 EF6 一起使用,请将此部分中的程序集和程序包名称替换为以下内容:

为 EF6 配置连接器/NET 支持:

  1. 编辑 app.config文件中的配置部分以添加连接字符串和连接器/NET 提供程序。

    <connectionStrings>
        <add name="MyContext" providerName="MySql.Data.MySqlClient"
            connectionString="server=localhost;port=3306;database=mycontext;uid=root;password=********"/>
    </connectionStrings>
    <entityFramework>
        <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework"/>
        <providers>
            <provider invariantName="MySql.Data.MySqlClient"
                type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.EntityFramework"/>
            <provider invariantName="System.Data.SqlClient"
                type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer"/>
        </providers>
    </entityFramework>
  2. 使用以下技术之一应用程序集引用:

    • NuGet 包。  安装 NuGet 包以在安装过程中自动将此引用添加到app.configor web.config文件中。例如,要安装 Connector/NET 8.0.22 的包,请使用以下安装选项之一:

      • 命令行界面 (CLI)

        dotnet add package MySql.Data.EntityFramework -Version 8.0.22
      • 包管理器控制台 (PMC)

        Install-Package MySql.Data.EntityFramework -Version 8.0.22
      • 带有 NuGet 包管理器的 Visual Studio。对于此选项,选择nuget.org作为包源,搜索 mysql.data并安装稳定版本的 MySql.Data.EntityFramework.

    • MySQL 安装程序或 MySQL Connector/NET MSI 文件。  安装 MySQL Connector/NET,然后将 MySql.Data.EntityFramework程序集的引用添加到您的项目中。根据使用的 .NET Framework 版本,程序集取自 v4.0v4.5v4.8文件夹。

    • MySQL 连接器/NET 源代码。  从源构建连接器/NET,然后将以下数据提供者信息插入到 app.configor web.config文件中:

      <system.data>
         <DbProviderFactories>
           <remove invariant="MySql.Data.MySqlClient" />
           <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" 
                type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.22.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
         </DbProviderFactories>
      </system.data>
      重要的

      始终更新版本号以匹配 MySql.Data.dll程序集中的版本号。

  3. 为 MySQL设置新DbConfiguration类。此步骤是可选的,但强烈推荐,因为它添加了 MySQL 类的所有依赖项解析器。这可以通过三种方式完成:

    • DbConfigurationTypeAttribute 在上下文类中 添加:

      [DbConfigurationType(typeof(MySqlEFConfiguration))]
    • DbConfiguration.SetConfiguration(new MySqlEFConfiguration())在应用程序启动时 调用。

    • DbConfiguration在配置文件中 设置类型:

      <entityFramework codeConfigurationType="MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.EntityFramework">

    也可以创建自定义 DbConfiguration类并添加所需的依赖项解析器。

EF6 功能

以下是在 Connector/NET 中实现的 Entity Framework 6 中的新功能:

  • Connector/NET 8.0.22 中的跨平台支持实现了 EF 6.4 作为初始提供程序版本,以包括 Linux 和 macOS 与 Microsoft .NET Standard 2.1 的兼容性。

  • 异步查询和保存添加了对自 .NET 4.5 以来可用的基于任务的异步模式的支持。Connector/NET 支持的新异步方法有:

    • ExecuteNonQueryAsync

    • ExecuteScalarAsync

    • PrepareAsync

  • 连接弹性/重试逻辑 支持从瞬时连接故障中自动恢复。要使用此功能,请添加到 OnCreateModel方法中:

    SetExecutionStrategy(MySqlProviderInvariantName.ProviderName, () => new MySqlExecutionStrategy());
  • 基于代码的配置让您可以选择在代码中执行配置,而不是像传统做法那样在配置文件中执行。

  • 依赖解析引入了对服务定位器的支持。一些可以用自定义实现替换的功能已经被排除在外。要添加依赖解析器,请使用:

    AddDependencyResolver(new MySqlDependencyResolver());

    可以添加以下解析器:

    • DbProviderFactory -> MySqlClientFactory

    • IDbConnectionFactory -> MySqlConnectionFactory

    • MigrationSqlGenerator -> MySqlMigrationSqlGenerator

    • DbProviderServices -> MySqlProviderServices

    • IProviderInvariantName -> MySqlProviderInvariantName

    • IDbProviderFactoryResolver -> MySqlProviderFactoryResolver

    • IManifestTokenResolver -> MySqlManifestTokenResolver

    • IDbModelCacheKey -> MySqlModelCacheKeyFactory

    • IDbExecutionStrategy -> MySqlExecutionStrategy

  • 拦截/SQL 日志记录提供低级构建块,用于拦截 Entity Framework 操作,并在顶部构建简单的 SQL 日志记录:

    myContext.Database.Log = delegate(string message) { Console.Write(message); };
  • DbContext 现在可以使用已经打开的 DbConnection 创建,这使得在创建上下文时打开连接会很有用(例如,当您无法保证连接状态时在组件之间共享连接)

      [DbConfigurationType(typeof(MySqlEFConfiguration))]
      class JourneyContext : DbContext
      {
        public DbSet<MyPlace> MyPlaces { get; set; }
    
        public JourneyContext()
          : base()
        {
    
        }
    
        public JourneyContext(DbConnection existingConnection, bool contextOwnsConnection)
          : base(existingConnection, contextOwnsConnection)
        {
    
        }
      }
    
      using (MySqlConnection conn = new MySqlConnection("<connectionString>"))
      {
        conn.Open();
        ...
    
        using (var context = new JourneyContext(conn, false))
        {
          ...
        }
      }
  • 改进的事务支持提供对框架外部事务的支持以及在实体框架内创建事务的改进方法。从 Entity Framework 6 开始, Database.ExecuteSqlCommand()默认情况下会将命令包装在一个事务中(如果命令尚不存在)。此方法有重载,允许用户根据需要覆盖此行为。通过诸如此类的 API 执行模型中包含的存储过程 ObjectContext.ExecuteFunction()。也可以将现有事务传递给上下文。

  • DbSet.AddRange/RemoveRange提供了一种优化的方式来从集合中添加或删除多个实体。

代码优先功能

以下是 Connector/NET 支持的新代码优先功能:

  • 支持插入/更新/删除存储过程的代码优先映射

    modelBuilder.Entity<EntityType>().MapToStoredProcedures();
  • 幂等迁移脚本允许您生成一个 SQL 脚本,该脚本可以将任何版本的数据库升级到最新版本。为此,请 Update-Database -Script -SourceMigration: $InitialDatabase在程序包管理器控制台中运行命令。

  • 可配置的迁移历史表 允许您自定义迁移历史表的定义。

使用 EF6 的示例

以下 C# 代码示例表示 Entity Framework 6 模型的结构。

using MySql.Data.Entity;
using System.Data.Common;
using System.Data.Entity;

namespace EF6
{
  // Code-Based Configuration and Dependency resolution
  [DbConfigurationType(typeof(MySqlEFConfiguration))]
  public class Parking : DbContext
  {
    public DbSet<Car> Cars { get; set; }

    public Parking()
      : base()
    {

    }

    // Constructor to use on a DbConnection that is already opened
    public Parking(DbConnection existingConnection, bool contextOwnsConnection)
      : base(existingConnection, contextOwnsConnection)
    {

    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
      base.OnModelCreating(modelBuilder);
      modelBuilder.Entity<Car>().MapToStoredProcedures();
    }
  }

  public class Car
  {
    public int CarId { get; set; }

    public string Model { get; set; }

    public int Year { get; set; }

    public string Manufacturer { get; set; }
  }
}

下面的 C# 代码示例显示了如何在将数据存储在 MySQL 表中的应用程序中使用先前模型中的实体。

using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;

namespace EF6
{
  class Example
  {
    public static void ExecuteExample()
    {
      string connectionString = "server=localhost;port=3305;database=parking;uid=root";

      using (MySqlConnection connection = new MySqlConnection(connectionString))
      {
        // Create database if not exists
        using (Parking contextDB = new Parking(connection, false))
        {
          contextDB.Database.CreateIfNotExists();
        }

        connection.Open();
        MySqlTransaction transaction = connection.BeginTransaction();

        try
        {
          // DbConnection that is already opened
          using (Parking context = new Parking(connection, false))
          {

            // Interception/SQL logging
            context.Database.Log = (string message) => { Console.WriteLine(message); };

            // Passing an existing transaction to the context
            context.Database.UseTransaction(transaction);

            // DbSet.AddRange
            List<Car> cars = new List<Car>();

            cars.Add(new Car { Manufacturer = "Nissan", Model = "370Z", Year = 2012 });
            cars.Add(new Car { Manufacturer = "Ford", Model = "Mustang", Year = 2013 });
            cars.Add(new Car { Manufacturer = "Chevrolet", Model = "Camaro", Year = 2012 });
            cars.Add(new Car { Manufacturer = "Dodge", Model = "Charger", Year = 2013 });

            context.Cars.AddRange(cars);

            context.SaveChanges();
          }

          transaction.Commit();
        }
        catch
        {
          transaction.Rollback();
          throw;
        }
      }
    }
  }
}