数据点

功能强大的(免费)实体框架工具

Julie Lerman

Julie Lerman

实体框架是开源的,因此开发社区可以在 entityframework.codeplex.com 上共享代码。 但是不要将自己局限在那里寻找工具和扩展。 还有其他一些不错的商业和社区驱动的工具,它们能帮助您使用实体框架取得更大成就。 在本专栏中,我想强调一些来自社区的工具和扩展。 它们是可通过 Visual Studio 库获得的免费 IDE 扩展,或可通过 NuGet 获得的免费代码库。 甚至有一些来自特别项目和官方位外部的实体框架团队的成员。

EntityFramework Reverse POCO Code First 生成器

仍然有很多人认为 Database First 工作流是从现有数据库创建实体框架模型的唯一方法。 该特定工作流创建 EDMX 文件,使您在设计器中与模型进行交互。 使用设计器还意味着您依赖代码生成器来创建实体类。 但是凭借反向工程工具,还可以使用带有已存在数据库的 EF Code First 来进行。 现在有三种方法可完成这项工作。 我最喜欢的是这个模板:由 Simon Hughes 开发的 EntityFramework Reverse POCO Code First 生成器 (efreversepoco.codeplex.com)。 公平地讲, 另外两个也是我广泛使用的 Microsoft 创建的工具。 一个是 EF Power Tools Beta 的功能。关于该功能的使用,请您参阅我 2011 年 5 月的博客文章,网址为 bit.ly/1ky2SAJ。 另一个是 Visual Studio EF 设计器的增强功能,包含在 Entity Framework 6.1 Tools for Visual Studio 的版本中。 现在 EF 设计器包含从现有数据库创建 POCO 的可视化方法,并能够通过选择要包含在模型中的表,来解决困扰我很久的一个问题。 我在 2014 年 2 月的博客文章中演示了此功能,网址为 bit.ly/1rOqUv4

Hughes 的模板于 2012 年 10 月首次发布于 CodePlex,它可以从一开始就选择数据库的子集。 事实上,这个工具似乎提供了我希望在 EP Power Tools 或更新的 RF6.1 设计器中添加或改进的所有功能。 不仅是我有这种感觉:在我写这篇专栏的时候,EntityFramework Reverse POCO Code First 生成器已经有近 77,000 次下载。

您可以通过 Visual Studio 的“扩展和更新”功能安装此模板,并支持以后项目脱机访问该模板,您也可以在想从数据库生成 POCO 的任何情况下,从 Web 直接使用该模板。 在添加模板之前,应将实体框架添加到项目,并在配置文件中明确指定数据库连接字符串,因为该模板依赖于这些条件。 一旦拥有 EF 和连接字符串,您就可以通过将新项添加到 Visual Studio 中的项目来启动模板。 如果已经安装了此模板,您可在“已安装”下找到它;否则,请打开“联机”部分。 请注意,在图 1 中,我并没有筛选模板,但在按最热门项目排序的列表中,EntityFramework Reverse POCO Code First 生成器排在首位。 那个位置确实给我留下了深刻的印象。

 图 1 获取 EntityFramework Reverse POCO Code First 生成器
图 1 获取 EntityFramework Reverse POCO Code First 生成器

选择此项会在项目中生成一个新 database.tt 模板文件。 此模板具有大量已应用默认设置的配置。 在图 2 中,您可以看到模板文件的一部分,其中显示一些可用于影响所生成代码的更简单配置。 ElementsToGenerate 设置让您能够控制所生成类的类型(POCO、上下文、Fluent 配置、UnitOfWork)。 此配置的一个好处就是,您可以为不同的项目生成不同的项。 您可以控制命名空间及类型的命名方式。 您可以生成一些基本的 Windows Communication Foundation (WCF) 类等。 此配置还可以使您通过使用 Regex 指定表模式或视图名称,从而指定应包含/排除的数据库表。 对我来说,指定对象是一个重要的功能。 但是,既然 EF6.1 设计器支持直观地选择数据库对象,以便创建 Code First 模型,我发现相比之下,在 .tt 文件中指定对象名称略显笨拙。

A Section of the EntityFramework Reverse POCO 生成器配置文件的一部分
图 2 EntityFramework Reverse POCO 生成器配置文件的一部分

Reverse POCO Code First 生成器与 EF Power Tools 的反向工程功能相比的另一个显著的优点就是性能。 但是,新 EF6.1 设计器显著加快了代码生成,因此对我来说,这不再是一个决定性因素。 尽管如此,Reverse POCO Code First 生成器有很多不错的功能(例如,包括将列的默认值作为属性的默认值)和自定义类生成方式的方法(例如,指定用于导航属性的集合的类型),我仍然喜欢它。 我只是希望能将它与 EF 设计器合并。

此模板中的功能太多,我在这里不一一列出它的功能,而是向您展示文档和 30 分钟的演示视频 (reversepoco.com),以便您能对此模板的使用有一个最佳的抢先体验。 您还可以看到,Hughes 已经接受了在 CodePlex 上向项目发布的内容。 请记住这一重要事项:EntityFramework Reverse POCO Code First 生成器当前适用于 SQL Server 和 SQL Server CE。 EF6.1 设计器适用于这些数据库以及任何其他更新到适于与 EF6.1 结合使用的数据库提供程序。 我听说 Hughes 生成器的下一代主要版本 (v3) 将可以像 EF6.1 设计器一样,对其他数据库(例如 Oracle、MySQL 等)进行反向工程。

实体框架实用程序

我将着重介绍的下一个库是在 GitHub 上由 Mikael Eliasson 维护的库 (bit.ly/UeWHGW)。 您可以在 NuGet 上搜索 EFUtilities 获取项目中的实用程序。

这组实用程序专注于性能和添加长期被实体框架开发人员遗漏的功能:在数据库中执行批量插入、更新和删除操作的能力。 这些实用程序中的 EFBulkOperation 类适用于 DbContext,因此您不能将其与 ObjectContext 一起使用。 尽管很多开发人员都已攻克了这一难题,但我已经认识 Mikael 一段时间了,因此很自然就想到他的产品。 在 CodePlex 网站上还有关于实体框架在本地 API 中获取批量操作最佳方法的讨论。

该实用程序允许您在 EFBulk­Operation 类上编写 LINQ 查询,以执行相关操作。 作为测试,我从 AdventureWorks2012 数据库创建了一个模型(使用我刚才讨论的 EntityFramework Reverse POCO Code First 生成器工具)。 然后,我在代码中创建了一个有 1,000 个 BusinessEntity 类型实例的列表:

var entityList = new List<BusinessEntity>();
for (int i = 0; i < 1000; i++)
{
  entityList.Add(new BusinessEntity{});
}

我使用标准实体框架代码插入这些实例,然后调用 SaveChanges:

using (var context = new AWModel()) {
  context.BusinessEntities.AddRange(entityList);
  context.SaveChanges();
}

这引发了 1,000 条单个插入语句发送到我的 SQL Server 数据库,整个操作通过一些重复执行,平均耗时约 6 秒。 我已经从这个计时中扣除了 EF 的预热时间。

然后,我使用了 EFBatchOperation 类插入 1,000 个相同的 BusinessEntity 对象:

using (var context2 = new AWModel()) {
  EFBatchOperation.For(context2, 
    context2.BusinessEntities).InsertAll(entityList);
}

平均执行大约 12 毫秒。

请记住,在标准 EF 代码中,我必须将所有实例添加到上下文,以便更改跟踪器决定需要哪个 SQL 发送到服务器。 另一方面,EFBatchOperation 类不依赖于 EF 更改跟踪器来创建 SQL。 相反,它会创建 EFUtilities 中的一个 DataReader 类,并使用早在 ADO.NET 2.0 中引进的 .NET SqlBulkCopy 类来将其传送到数据库。 我很高兴看到这一点,因为这就是我通常所推荐的解决这个问题的路径。 因此,这种方法不仅不强迫上下文跟踪 1,000 个实体,它还发送单一命令到数据库以及要插入数据的二进制流。 SQL 事件探查器不会显示此流,因此我在 SQL 事件探查器中所看到的作为 EFBatchoperation.InsertAll 方法结果的就是这个单一命令:

insert bulk BusinessEntity
  ([rowguid] UniqueIdentifier,
  [ModifiedDate] DateTime)

这里有个很好的保护,如果您正在使用的数据库提供程序没有批量方法,实用程序将恢复到处理该请求的 EF 默认方法。 在这种情况下,这意味着将发送 1,000 个插入命令到数据库。

EFBatchOperation 还具有进行批量更新和批量删除的方法,对其中的每个来说,好消息是:您不必像在 EF 中一样事先加载实体到内存或上下文。 相反,这里有一个如何使用 EFBatchOperation 从数据库删除选择行的示例:

EFBatchOperation.For(context, context.BusinessEntityContacts).Where(
  p => p.ContactTypeId == 11).Delete();

它发送相关的 SQL 到数据库:

DELETE FROM [Person].[BusinessEntityContact] WHERE 11 = [ContactTypeID]

批量操作命令已经提供了显著的性能提升,现在 Eliasson 正致力于研究 DbContext.Include 方法的替代方案,以提升预先加载的性能。 EF 实用程序中方法的目标是“转为将每一个‘已包括’集合加载为单独查询,然后手动修复这些关系。 同时还提供子集合筛选、排序和分页(如有可能的话)。”,而不是平铺数据或将资源浪费在结果中的大量冗余数据上。您可以详细了解此功能,并可在 bit.ly/1hBsGf5 上密切关注其发展。 请不要忽视此项目的 readme.md 文件。

Entity Framework 6 Contrib

Entity Framework 6 Contrib 是由 Microsoft MVP Unai Zorrilla 在 CodePlex 上开展的项目 (ef6contrib.codeplex.com)。 Zorrilla 已经向 Microsoft EF 程序包发布了多项内容,例如“自定义复数化”、AddRange 和 RemoveRange 以及 AddFrom­Assembly,我已在之前的专栏或文章中撰写了所有这些功能。 Zorrilla 拥有在 ef6contrib.codeplex.com 网站上维护的少数额外帮助程序,包括:

  • 预配置的西班牙语复数化服务(这已合并到我之前讨论的 EntityFramework Reverse POCO Code First 生成器项目中)。
  • 可在运行时将 Fluent API 配置添加到 Code First 模型的两个更加便捷的方法。
  • 不包含在 EF 中的、可以与 Code First 迁移一起使用的一些操作,例如 DatabaseCollation、GrantTablePermission 和 RevokeTablePermission。
  • 一个适用于迁移的称为 DropAndMigrateDatabaseToLatestVersion 的初始化表达式。
  • 一组利用 EF6 侦听器和依赖关系解析程序的查询分析器类型。

我一直非常喜爱 Zorrilla 对 EF 的新增功能的原因在于,他在工作中采用自己编写的代码,反复使用,并将其封装与他人分享。 尤其是分析器,非常酷。 您可以设置参数,通过参数您可以衡量 EF 数据库执行的性能,监视代码与您期望的符合程度。 还有一个分析器可检查非参数化查询。 如果您将侦听器添加到 DbConfiguration 类(另一个 EF6 功能),它将分析来自给定 DbContext 的所有命令。 CodePlex 项目有大量让您研究分析器的测试和示例,以及包含在 Entity Framework 6 Contrib 中的其他工具。 图 3 显示添加 PerformanceInterceptor 的示例中的一个 DbConfiguration 类,然后指定其在集合中检查四个分析器中的三个。

图 3 从 Entity Framework 6 Contrib 配置分析器

public class Configuration : DbConfiguration  {
    public Configuration() {
      this.AddDependencyResolver(new CustomResolver());
      this.AddInterceptor(new PerformanceInterceptor((msg =>
      {
        Console.WriteLine(msg.Message);
      })));
    }
    private class CustomResolver : IDbDependencyResolver{
      public object GetService(Type type, object key) {
        return null;
      }
      public IEnumerable<object> GetServices(Type type, object key) {
        // You can use here your preferred IoC container
        if (typeof(IPerformanceAnalyzer).IsAssignableFrom(type)) {
          return new List<IPerformanceAnalyzer>(){
             new ExecutionTimePerformanceAnalyzer(TimeSpan.FromSeconds(3)),
             new UnparametrizedWhereClausesPerformanceAnalyzer(),
             new TopSlowQueriesPerformanceAnalyzer(10)
          };
        }
        return Enumerable.Empty<object>();
      }
    }
  }

根据此配置,控制台窗口将显示来自分析器的所有报告。

丰富的 EF 扩展和工具

我发现只有一小部分工具对我的工作确实有益。 我希望您没有忽视其他工具,但在这里我没有空间来深入介绍所有工具。 Erik Ejlskov Jensen 在他的博客文章中有一个更值得关注的扩展的列表:“Entity Framework 6 (& SQL Server Compact) (5)–Entity Framework 6 Extensions”(bit.ly/SxxPc5)。 该列表包括为 EF6 启用二级缓存 (bit.ly/UePlmU) 的 EF 缓存提供程序的更新版本、用于替代停止使用的自跟踪实体 (trackable.codeplex.com) 的可跟踪实体以及一些解决批量操作功能的其他项目。 Ejlskov Jensen 是 SQL Server CE MVP,除了直接对 EF6 发布了很多强大的 SQL CE 相关功能外,他还发布了一个广受欢迎的 Visual Studio 扩展 SQL Server Compact Toolbox,其中包括许多功能可帮助使用 EF。 无论您是使用带 EF 的 SQL CE 还是使用不带 EF 的 SQL CE,这个工具箱都是一个重要的扩展。 最后,我想向 Jimmy Bogard 在一系列名为“Missing EF Feature Workarounds”(缺失 EF 功能的解决方法)的博客文章中所做的工作致以真诚的感谢,其中还包括将 EF6 扩展为向 EF 模型提供全局筛选器的方法,且可作为 NuGet 程序包提供。 您可以在他的博客文章中了解更多详细信息,网址为 bit.ly/1prZv0a


Julie Lerman 是 Microsoft MVP、.NET Framework 导师和顾问,住在佛蒙特州的山区。您可以在全球的用户组和会议中看到她对数据访问和其他 .NET 主题的演示。她是《Programming Entity Framework》(2010) 以及“代码优先”版 (2011) 和 DbContext 版 (2012)(均出自 O’Reilly Media)的作者,博客网址为 thedatafarm.com/blog。通过她的 Twitter(网址为 twitter.com/julielerman)关注她,并在 juliel.me/PS-Videos 上观看其 Pluralsight 课程。

衷心感谢以下技术专家对本文的审阅:Mikael Eliasson、Simon Hughes 和 Unai Zorrilla