.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計劃中
相關文章
相關標籤/搜索