数据点

EF6、EF7 与 ASP.NET 5 组合

Julie Lerman

Julie Lerman在 2015 年 1 月的数据点专栏“迎接 Entity Framework 7”中,我通过查看截至撰写本文时 EF7 Alpha 的状态,重点介绍了 EF7 中即将推出的新功能。在 2014 年 12 月,就在包含该专栏的那期即将出版之际,EF 团队做出了分期发布 EF7 的重要决定。我设法在最后一分钟将这两句话写进文章中:“根据 bit.ly/1ykagF0 上的博文《EF7 — 优先级、关注和首次发布》,首版 EF7 将关注与 ASP.NET 5 的兼容性。后续版本将添加更多功能。”

从那时起,我发现,要准确地理清这些版本,着实会让人有点困惑。我希望确保您充分了解 EF7 首次发布的目标受众,您是否应使用此版本,以及您还有哪些选择。

这将有助于讨论 Microsoft .NET Framework 的下一版本与 ASP.NET 5 应用程序的运行平台之间的差异。我将提供深入剖析,但请查看 2015 年 3 月刊中 Daniel Roth 的文章“深入探究 ASP.NET 5 运行时”来获得更为深入的了解。然后,我将介绍 EF6 和 EF7 如何适应这一混合环境。

ASP.NET 5(又称 ASP.NET vNext)设计为更少地依赖完整的 .NET Framework,甚至更少地依赖于 Windows 本身。在 ASP.NET 5 范畴内,您可以使用 ASP.NET MVC 6、类库、甚至是控制台应用中新组合的功能集来创建 Web 应用程序和 Web API。如果您认为 ASP.NET 是一种创建网站的方法,则控制台应用很神秘。但是,ASP.NET 5 确实提供了 .NET Framework 的新“版本”,可供应用程序在其上运行。事实上,这一新版本当前具有两种类型:一种类型在完整的公共语言运行时 (CLR) 上运行,另一种类型在新的 CoreCLR 上运行。第三种类型将在跨平台 CLR 上运行,并在不久的将来添加。最精简的版本称为 .NET 核心,并在简化的运行时 CoreCLR 中运行。如果您回想 .NET Framework 的早期课程,可能还记得 Microsoft 定义的公共语言基础结构 (CLI),这是一种规范。然后,Microsoft 基于 CLI 生成了 CLR。Mono 是运行在 Linux 和 Mac OS X 上的 CLI 的另一个实现。.NET 应用程序始终依赖于 CLI 实现的可用性。我们大多数人已经使用过 Visual Studio 和 Windows 来创建依赖于 CLR 的 .NET 应用。Xamarin 已变得越来越流行用于开发可在 Mono 上运行的应用程序。

CoreCLR 和实体框架

现在,Microsoft 创建了称为 CoreCLR 的 CLI 的简化实现。CoreCLR 不仅是开放源代码,而且还能由 NuGet 和其他程序包管理器进行分发。这使开发人员能够创建非常适合移动设备和基于云的服务器应用程序的轻型应用程序。并且 CoreCLR 还删除了只能将应用程序部署到 Windows 计算机这一约束。

Entity Framework 6(及更早版本)依赖于完整的 .NET Framework 和完整的 CLR,并且不会运行面向 CoreCLR 的应用程序。但 EF7 已经设计为一组较小且可组合的 API,此组 API 可基于您所需的功能集进行混合和匹配。例如,如果您面向的是非关系数据存储,则无需旨在用于关系数据库的关系功能或迁移功能。使用 EF7 时,该逻辑位于一个单独的 DLL 中,您无需检索此 DLL 并将其加载到内存中。

当您选择面向 CoreCLR 时,可以通过选择正确的运行时环境执行此操作。为了使用 ASP.NET 5,Microsoft 创建了 K 运行时环境 (KRE),如 ASP.NET MVP Gunnar Peipman 所述,“....... 是启动和运行 ASP.NET vNext 应用程序所需的代码。在发布之前的某个时刻将重命名 K 运行时。请密切关注这一更改。例如,KVM(版本管理器)将变成 DNVM(.NET 版本管理器)。运行时包括诸如编译系统、SDK 工具和本机 CLR 宿主”(bit.ly/1x9rpOn)。有 32 位和 64 位版本的 KRE 支持 CoreCLR。您可以将这些看作是图 1 中的 ASP.NET 5 应用程序目标选项。

在 ASP.NET 5 应用程序中选择目标 KRE
图 1 在 ASP.NET 5 应用程序中选择目标 KRE

除在我的 Pluralsight 课程“迎接 Entity Framework 7”(bit.ly/18ct13F) 中观看演示视频外,在某个应用程序中查看 EF7 的最简单的方法就是使用 ASP.NET 5 项目模板,这会给您一个已绑定 EF7 的解决方案,从而可对您的应用程序提供基于身份的身份验证。

首先选择 ASP.NET Web 应用程序,然后选择 ASP.NET 5 Starter Web 模板,如图 2 中所示。

使用预配置的 Entity Framework 7 创建一个新的 ASP.NET 5 应用程序
图 2 使用预配置的 Entity Framework 7 创建一个新的 ASP.NET 5 应用程序

您会发现已经选择了 EF7 EntityFramework.SqlServer 和 EntityFramework.Commands 数据包。SqlServer 数据包将导致提取其依赖关系(EntityFramework.Core、EntityFramework.Relational)。

您可以在 project.json 文件中查看这一内容,这在其依赖关系部分中列出了供 NuGet 检索的数据包。下面是该部分中的几行:

"dependencies": {
  "EntityFramework.SqlServer": "7.0.0-beta2",
  "EntityFramework.Commands": "7.0.0-beta2",
  "Microsoft.AspNet.Mvc": "6.0.0-beta2",

您还可以查看参考资料部分结尾的内容,默认情况下,其中列出了适用于 ASP.NET 5.0 和 ASP.NET Core 5.0 版本的应用程序的引用。图 3 显示扩展了 ASP.NET Core 5.0 引用的列表。请注意,尽管在更高版本中,Migrations 已成为 Relational API 的一部分,但因为模板当前针对的是 beta2 版本,您会发现引用了 Migrations 数据包。

和 ASP.NET Core 5.0 引用均包含 Entity Framework 7 数据包和 API
图 3 ASP.NET 5.0 和 ASP.NET Core 5.0 引用均包含 Entity Framework 7 数据包和 API

ASP.NET 5 和 EF7 都使用并严重依赖于依赖关系注入 (DI)。您可以在图 4 的该项目的 Startup.cs 中发现存在逻辑,使得应用程序知道它应使用实体框架、其 SQL Server API 和用于安全、ApplicationDbContext 的单个预定义 DbContext。您会发现相同的上下文被绑定起来作为应用程序的 Identity 服务。连接字符串存储在 config.json 文件中,Startup 构造函数绑定该文件,以便此应用程序能够在需要时找到该文件。

图 4 默认 Startup.cs 的部分列表

public class Startup {
  public Startup(IHostingEnvironment env) {
    Configuration = new Configuration()
      .AddJsonFile("config.json")
      .AddEnvironmentVariables();
  }
  public IConfiguration Configuration { get; set; }
  public void ConfigureServices(IServiceCollection services) {
    services.AddEntityFramework(Configuration)
      .AddSqlServer()
      .AddDbContext<ApplicationDbContext>();
    services.AddIdentity<ApplicationUser, IdentityRole>(Configuration)
      .AddEntityFrameworkStores<ApplicationDbContext>();
       services.AddMvc();  }

项目模板现在设置您默认使用 EF7 并且使用其逻辑,无论您面向的是简化的 CoreCLR 还是完整的 .NET Framework。事实上,在默认情况下,该项目并未定义为在 CoreCLR 下运行,而是定义为使用完整的 .NET Framework。那么,让我们看一下此选项适用的情况。

适用于 ASP.NET 5(和实体框架)的完整 CLR

对即将发布的 .NET Framework,即 .NET Framework 4.6,有一个更新,此更新将允许您继续创建所有已经能够创建的软件样式 — 从 Windows 窗体和 Web 窗体到 Windows Presentation Foundation (WPF) 和 ASP.NET MVC 5 应用程序。由于如图 1 中所示的其他 KRE 版本(KRE-CLR-amd64 和 KRE-CLR-x86),还可以在完整的 .NET Framework 上运行 ASP.NET 5 应用程序。这让您鱼与熊掌兼得,如果您想要从 ASP.NET 5 的其他新功能(如 MVC 6 或新的 Web API)中受益,则需要将 MVC 控制器和 Web API 合并。它还使您具有向后兼容性,因为您面向的是完整的 .NET Framework 和 CLR,并且可以访问它的全套功能。默认情况下,project.json 文件指示您希望能够在 CLR 的任一版本下运行应用程序:

"frameworks": {
      "aspnet50": { },
      "aspnetcore50": { }
    },

您可以完全删除“aspnetcore50”行(以及上一行末尾的逗号),并且效果是双方面的。第一方面,ASP.NET Core 5.0 部分将从“解决方案资源管理器”中的“引用”中消失。第二方面,图 1 中显示的 KRE 目标会简化为仅 KRE-CLR 选项。

您将仍然面向 EF7 并获得这一新版本的所有好处。但是,还有一点尚不明确,即因为 KRE-CLR 面向的是完整的 .NET Framework,它与 Entity Framework 6 兼容。这意味着如果您有现有的 EF6 逻辑,则有可能将其与 ASP.NET 5(尽管不是 CoreCLR 版本)结合使用,并获得 ASP.NET 5 新功能的好处,而无需重新编写您的 EF6 代码来与 EF7 保持一致。

使用 Ef6 的 ASP.NET 5(完整 CLR)

认识到这一点是可能的,当然我必须设法将 EF6 融入到 ASP.NET 5 解决方案。请记住,我为实现此目的的这一方法很可能需要在我正在使用的最早版本(2015 年 2 月版)与 ASP.NET 5 和 Visual Studio 2015 发布时的版本之间进行更改。

我发现实现此目的的最重要因素就是,让实体框架逻辑和依赖关系位于 ASP.NET 5 项目的单独项目中。不管怎样,我通常会以这种方式设计我的应用程序,而不是让应用程序的所有层都位于单个项目。使用专用于 EF 的单独项目,您不必担心 ASP.NET 5 应用程序动态引入 EF7 API。

您最好使用 ASP.NET 空项目模板,而不是从 ASP.NET 5 Starter Web 项目入手。Project.json 文件只有一个指定的依赖关系 Microsoft.Asp.NET.Server.IIS。

接下来,创建第二个项目。我选择 .NET 4.6 类库项目。然后,通过确保我指向 nuget.org 作为数据包来源,并指向类库作为默认项目,来使用程序包管理器控制台安装 EF6。此时,我就能够像以前一样调用 install-package entityframework。这将 entityframework.dll 和 entityframework.sqlserver.dll 引入项目。

为了进行测试,我创建了一个简单的类,Ninja.cs(因为我将 EF6 视为 Ninja 版本,而将 EF7 视为 Samurai 版本):

public class Ninja {
    public int Id { get; set; }
    public string Name { get; set; }
  }

并创建了公开 Ninjas 的 DbSet 的 DbContext:

public class NinjaContext : DbContext {
  public NinjaContext()
    :base(@"Data Source=(localdb)\mssqllocaldb;
          Initial Catalog=NinjaContext;
          Integrated Security=True;"){ }
  public DbSet<Ninja> Ninjas { get; set; }
}

为简单起见,在测试中,我还直接将 SQL Server 连接字符串硬编码到我的 DbContext 类中。

通过我定义的模型,我可以像以前一样启用迁移、添加新迁移和创建数据库。请注意,在我将 EF6 安装到数据项目时,我保留了数据包创建的 app.config 文件,因此我可以将该项目设置为启动项目,然后迁移命令将能够正常工作。我还使用了 DbMigrationsConfiguration Seed 方法来将几个 Ninjas 预先植入到数据库。

我不想直接从 ASP.NET 5 应用程序进行 EF 调用,造成版本混淆,因此我在 EF6 项目有一个类,其中封装我需要的查询。我称其为“Repository”,因为它符合我的目的,但这并不是一个遵循存储库模式的类。一个演示应用程序和单个查询并不构成挑战,但您可以使用最喜欢的模式来分散更为复杂应用程序的问题:

public class Repository  {
  public List<Ninja> GetAllNinjas() {
    using (var context=new NinjaContext())
    {
      return context.Ninjas.ToList();
    }
  }
}

随着数据访问项目设置完毕,我可以返回到 ASP.NET 5 项目。我已经创建了一个简单的控制器来检索并返回一个视图:

public class NinjaController : Controller
  {
    public IActionResult Index()
    {
      var repo = new Repository();
        return View(repo.GetAllNinjas());
      }
    }
  }

我还创建了一个简单的 Index.cshtml,它会列出由控制器传入的 Ninjas:

@model List<EF6Model.Ninja>
@{
  ViewBag.Title = "EF6 Ninjas";
}
@foreach (var item in Model)
{
  @Html.Label(item.Name);
}

最后,图 5 显示添加到 startup.cs 文件的代码,以便插入 MVC 服务,并指定到 Ninja 控制器的路由。

图 5 配置使用 Entity Framework 6 的 ASP.NET 5 应用程序的 Startup 类

public class Startup  {
  public void Configure(IApplicationBuilder app) {
    app.UseMvc(routes =>
    {
      routes.MapRoute(
      name: "default",
      template: "{controller}/{action}/{id?}",
      defaults: new { controller = "Ninja", action = "Index" });
     });
  }
  public void ConfigureServices(IServiceCollection services)  {
    services.AddMvc();
  }
}

我还从 project.json 文件中删除了 aspnetcore50 框架,以确保我只面向完整的 CLR。

凭借迁移所创建的数据库和某些种子数据,我能够看到 ASP.NET 5 应用程序使用基于 EF6 的项目,显示使用 EF6 检索过的数据(请参阅图 6)。

ASP.NET 5 应用程序通过 Entity Framework 6 显示数据
图 6 ASP.NET 5 应用程序通过 Entity Framework 6 显示数据

我能否立即使用 EF7 和 ASP.NET 5?

我对 EF7 的新功能非常感兴趣,并很喜欢学习如何充分利用它们。我还希望了解 ASP.NET 5 所开创的新的编程途径。CoreCLR 的功能将随着发展变得更加丰富,EF7 也同样如此。我对 EF7 的状态印象深刻,因为它将与 ASP.NET 5 一起发布。但是由于我不是一名 Web 开发人员,我没有理由研究前沿 ASP.NET 中的生产代码,而且 EF 团队已明确表示建议只通过 ASP.NET 5 应用程序使用最初发行的 EF7 版本,这一版本将被标记为预发布版本。否则,EF 团队的建议是,请等待,直到 EF7 作为真正的版本发布。因此,我也会等待 EF7 的后续版本,但会随着项目的进展继续追踪此项目并对此项目进行实验,以便了解更多我可能错过的与 EF6 功能同等的功能。

目前,CoreCLR 大大超出了我的研究范围。如果我能找到一种方案符合 ASP.NET 5 的完整 CLR 版本即将提供的功能,以及 EF7 预发布版本的功能集,那么我会希望采用该路径。然而,更有可能的是,我最终还是会等待后续版本。并且到那时,ASP.NET 5 和 CoreCLR 的功能也会更加丰富。就我个人而言,到那之前的一段时间是研究和了解这些工具以为更高版本做好准备的好机会。

在此期间,我强烈建议您阅读 Scott Guthrie 的博客文章“ASP.NET 5 简介”(网址为 bit.ly/1wV4zzm),以便更好地了解 ASP.NET 5 可提供的所有新功能。可组合的 API 只是开发人员将在这一转变中获得的众多令人兴奋的改进之一。


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

衷心感谢以下技术专家对本文的审阅:Rick Strahl
Rick Strahl 是位于美丽的夏威夷毛伊岛的 West Wind Technologies 的总裁。Rick 喜欢各种冲浪活动和极限运动。从需要手摇曲柄(或一对导线捻接器)实现联机的早期 Web 以来,他从事软件开发已长达 25 年以上,专注于构建业务应用程序和 Web 应用程序。如今,Rick 在前端使用 AngularJS,并在后端使用 ASP.NET 堆栈和 Microsoft 技术,来为使用 HTML5、JavaScript 和移动 Web 技术的用户构建以客户端为中心的 Web 应用程序和服务。Rick 的公司 West Wind Technologies 也生产大量开发人员相关的工具,包括 West Wind WebSurge、West Wind Web 监视器和 Html 帮助生成器。Rick 还维护位于 github.com/RickStrahl 的大量开放源代码库,您可以访问他在 weblog.west wind.com 开设的博客,或直接通过 rstrahl@west-wind.com 与他联系。