.Net Core 2.0生态(4):Entity Framework Core 2.0 特性介绍和使用指南

前言

这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升级EF也发展到EF6.x,Entity Framework Core是一个支持跨平台的全新版本,能够用三个词来概况EF Core的特色:轻量级、可扩展、跨平台。跨平台的特性是EF6.x没法替代的优点,也许会成为你在项目中技术选型的缘由之一。html

对于.NET Core 2.0的发布介绍,围绕2.0的架构体系,本系列相关文章:git

  1. .Net Core 2.0 生态(1).NET Standard 2.0 特性介绍和使用指南(已发布)
  2. .Net Core 2.0 生态(2).NET Core 2.0 特性介绍和使用指南(已发布)
  3. .Net Core 2.0 生态(3)ASP.NET Core 2.0 特性介绍和使用指南(已发布)
  4. .Net Core 2.0 生态(4)Entity Framework Core 2.0 特性介绍和使用指南(已发布)

获取和使用

在命令行工具安装NuGet包,好比:安装SQL Server EF Core提供程序,并指定版本为2.0.0github

$ dotnet add package Microsoft.EntityFrameworkCore.SqlServer -V 2.0.0

在VS2017中使用包管理器控制台安装sql

PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 2.0.0

在ASP.NET Core 2.0默认项目包含支持EF Core 2.0的SQL Server, SQLite, 和 in-memory数据库提供程序,建立项目无需额外添加。数据库

查看在不一样平台上使用EF Core指南,查看更多安装和升级细节,进入帮助文档api

新特性

如下是最显著的新特性:架构

  • 使用.NET Standard 2.0目标框架:这使得EF Core 2.0可支持多种.NET平台实现和应用程序类型,查看平台支持列表app

  • LINQ解析改进:EF Core 2.0中的查询更加高效,适应多种场景。举个例子,增长了翻译成SQL语句模式的数量限制,避免了在之前版本中由于客户端计算致使多重查询的问题。(优化了客户端计算性能)框架

  • Like查询支持:LINQ查询可使用EF.Functions.Like(),最终解析为SQL中的like语句,在必要的时候会进行内存计算,举个例子,下面的查询:less

    var customers =
    from c in context.Customers
    where EF.Functions.Like(c.Name, "a%");
    select c;

    解析成SQL语句:

    SELECT [c].[Id], [c].[Name]
    FROM [Customers] AS [c]
    WHERE [c].[Name] LIKE N'a%';

    和SQL中like语句同样使用通配符。

  • 实体包含关系和表拆分:能够经过属性关联创建实体之间的包含关系,这个特性和EF6中的复合类型相似,只须要定义一个导航属性。实体包含关系定义与表拆分结合使用,能够将两个实体自动映射为单张表,参看下面的示例:

    public class Customer
      {
          public int Id { get; set; }
          public string Name {get; set;}
          public PhysicalAddress Address { get; set; }
      }
    
      public class PhysicalAddress
      {
          public string StreetAddress { get; set; }
          public Location Location { get; set; }
      }
    
      ...
    
      modelBuilder.Entity<Customer>()
          .OwnsOne(c => c.Address);
  • 全局查询过滤器:在DbContext中为实体定义查询过滤器,下面代码在OnModelCreating方法中定义:

    modelBuilder.Entity<Post>()
      .HasQueryFilter(p => !p.IsDeleted);

    下面的查询只会返回未被标记为删除的结果:

    var blog = context.Blogs
    .Include(b => b.Posts)
    .FirstOrDefault(b => b.Id == id);

    这个特性在特殊的业务场景下将有大用处,好比多租户中数据过滤实现。

  • DbContext Pooling(池):这项特性可以显著提高Asp.net Core应用程序的性能,经过在服务注册DbContext类型时启用,使用预先建立的实例池,避免为每一个请求建立新实例:

    services.AddDbContextPool<BloggingContext>(
        options => options.UseSqlServer(connectionString));

    这是一个最佳实践,推荐使用!

  • SQL方法支持字符串插值:下面的SQL语句使用了C#中字符串插值语法,简化参数化查询:

    var city = "Redmond";
    
      using (var context = CreateContext())
      {
          context.Customers.FromSql($@"
              SELECT *
              FROM Customers
              WHERE City = {city}");
      }

    以上代码转换为SQL语句会建立一个名为@p0的参数,值为Redmond,生成以下SQL语句:

    SELECT *
      FROM Customers
      WHERE City = @p0
  • 更多特性:如:显式编译查询、自包含实体配置、数据库标准函数映射。还修复了许多Bug。详细内容参考:新功能

项目升级和核心API变化

  1. 将项目.NET平台设置为支持.NET Standard 2.0的平台
  2. 使用支持2.0的数据库提供程序
  3. 更新EF Core引用包(包括运行时和工具)到2.0
  4. 必要的时候对代码进行修改,具体参看核心变化

在2.0版本中,部分API和操做有较大改进,有部分改进须要修改现有程序代码,对于大多数应用程序来讲,影响不大,大多数状况下,只须要从新编译和最少的修改来替换过期的API。

获取应用程序服务新方式

注:EF Core在设计时的操做好比生成数据迁移代码,更新数据库,须要访问应用程序服务。设计时工具和应用程序存在调用关系。

推荐将ASP.NET Core Web应用程序更新到2.0,在ASP.NET Core 2.0在启动类以外初始化配置。在以前的版本EF Core尝试执行Startup.ConfigureServices,直接访问应用程序的服务提供者,使用EF Core的应用程序一般从配置文件中访问链接字符串,因此单靠Startup已经不能知足获取链接字符串的须要。

更新ASP.NET Core 1.x到2.0,当使用了EF Core工具,会收到以下错误提示:

No parameterless constructor was found on 'ApplicationContext'. Either add a parameterless constructor to 'ApplicationContext' or add an implementation of 'IDesignTimeDbContextFactory ' in the same assembly as 'ApplicationContext'

在ASP.NET Core 2.0默认模板中新增设计时支持,静态方法Program.BuildWebHost容许EF Core在设计时访问应用程序服务提供者,若是升级ASP.NET Core 1.x应用程序,同时升级Program

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace AspNetCoreDotNetCore2._0App
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}

注:若是没有ASP.NET Core 2.0应用程序没有更改Program启动方式,依然可使用实现IDesignTimeDbContextFactory<ApplicationContext>接口方式提供EF Core 2.0设计时支持,不推荐这么作。

IDbContextFactory更名

为了支持不一样的应用模式,在设计时提供对DbContext更多自定义控制,在之前的版本提供接口IDbContextFactory<TContext>,EF Core工具在设计时,会发现应用程序中该接口的实现并使用它来建立DbContext对象。

这个接口由于具备通用性的名称,容易误导开发者使用来处理须要从新建立DbContext的开发场景,当在设计时EF Core工具使用它时由于没有考虑到设计时的特殊环境能够致使Update-Databasedotnet ef database update命令执行失败。

基于以上的缘由,为了更精准的表达该接口的做用,咱们将其更名为IDesignTimeDbContextFactory<TContext>,在2.0中IDbContextFactory<TContext>仍然存在,可是已经标记为过期了。

DbContextFactoryOptions移除

由于ASP.NET 2.0的升级,咱们发如今接口IDesignTimeDbContextFactory<TContext>不在须要DbContextFactoryOptions
下面是你应该使用的替代方案。

  • DbContextFactoryOptions.ApplicationBasePath 使用AppContext.BaseDirectory代替
  • DbContextFactoryOptions.ContentRootPath 使用Directory.GetCurrentDirectory()代替
  • DbContextFactoryOptions.EnvironmentName 使用Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")代替

EF Core 2.0须要2.0数据库提供程序

对于EF Core 2.0,咱们已经对数据库提供程序的工做进行了许多简化和改进。1.0.x和1.1.x提供程序已经不能在EF Core 2.0下工做。

由EF团队开发的SQL Server和SQLite数据库提供程序,2.0版本将在2.0版本中提供。其余数据库提供程序也已经升级到2.0版本:

日志记录和诊断事件更改

注意:这些更改不会影响大多数的应用程序代码。

发送给ILogger的消息的事件ID在2.0中发生了变化。如今在EF Core中事件ID是全局惟一的。这些消息如今也遵循告终构化日志的标准模式,例如,MVC。

Logger类别也发生了变化,类别可经过DbLoggerCategory访问。

DiagnosticSource事件如今使用与对应ILogger消息相同的时间ID名称,事件ID、有效负载类型和类别进行了统一。

ID从Microsoft.EntityFrameworkCore.Infraestructure命名空间移到Microsoft.EntityFrameworkCore.Diagnostics

EF Core元数据关系API变化

EF Core 2.0为不一样的提供程序建立一个不一样的IModel,这一般对应用程序是透明的,从而简化了底层元数据API,使得任何对公共关系的元数据均可以经过调用来实现,对好比下代码,在1.1.x中代码:

var tableName = context.Model.FindEntityType(typeof(User)).SqlServer().TableName;

如今能够这么写

var tableName = context.Model.FindEntityType(typeof(User)).Relational().TableName;

更具通用性。

在好比使用方法ForSqlServerToTable,如今可使用更加通用的代码来实现

modelBuilder.Entity<User>().ToTable(
Database.IsSqlServer() ? "SqlServerName" : "OtherName");

请注意,此更改仅适用于为全部关系提供程序定义的API/元数据。当只针对单个提供者时,API和元数据仍然是相同的。举个例子,汇集索引是SQL Server特有的,因此ForSqlServerIsClustered.SqlServer().IsClustered()必须使用。

不要控制EF服务提供程序

EF Core使用内置IServiceProvider在框架内部实现,应用程序应该容许EF Core建立和管理这个提供程序。强烈建议删除全部UseInternalServiceProvider的调用,AddEntityFrameworkAddEntityFrameworkSqlServer不须要经过应用程序代码调用,建议移除。AddDbContext的使用方式和之前同样。

内存数据库必须命名

全局匿名内存数据库已经被删除,而全部内存数据库都必须被命名。

optionsBuilder.UseInMemoryDatabase("MyDatabase");

名称相同就算调用屡次,仍然使用同一个数据库,容许由多个上下文实例共享。

Read-only API 变化

IsReadOnlyBeforeSave, IsReadOnlyAferSave, 和 IsStoreGeneratedAlways 已通过时,由 BeforeSaveBehaviorAfterSaveBehavior代替。这些行为应用到任何属性(不只是存储生成的属性)并检测属性值如何被使用,好比插入数据库行(BeforeSaveBehavior)或更新现有数据库行(AfterSaveBehavior)。

属性经过ValueGenerated.OnAddOrUpdate进行标记,例如:计算列。默认状况下,将忽略当前设置在该属性上的任何值。这意味着不管是否在跟踪实体上设置或修改了任何值,都将始终得到一个存储生成的值。这能够经过设置不一样的Before\AfterSaveBehavior来改变。

新的ClientSetNull删除行为

在之前的版本中DeleteBehavior.Restrict经过上下文对实体有一个跟踪行为,这些实体与SetNull语义更加封闭。在EF Core 2.0中一个新的行为ClientSetNull做为可选关系的默认值。此行为为跟踪实体设置了SetNull语义,并限制使用EF Core建立的数据库的行为。根据咱们的经验这对跟踪实体和数据库是很是有用的。

设计时提供程序更改

Microsoft.EntityFrameworkCore.Relational.Design引用包移除,功能被整合进Microsoft.EntityFrameworkCore.RelationalMicrosoft.EntityFrameworkCore.Design引用包。

不一样数据设计时引用包,好比Microsoft.EntityFrameworkCore.Sqlite.Design, Microsoft.EntityFrameworkCore.SqlServer.Design,整合进其主提供程序中。

在EF Core 2.0中启用Scaffold-DbContextdotnet ef dbcontext scaffold 如今只须要引用单一的包

<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer"
    Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools"
    Version="2.0.0"
    PrivateAssets="All" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet"
    Version="2.0.0" />

下一步计划

已经在进行下一个版本的开发,查看开发计划,另外也在完成EF 6.2

遗憾的地方

  • 不支持GroupBy、延迟加载——这两个特性都在2.1计划中
相关文章
相关标签/搜索