.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無法替代的優勢,也許會成為你在項目中技術選型的原因之一。

對於.NET Core 2.0的發布介紹,圍繞2.0的架構體系,本系列相關文章:

  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.0

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

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

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指南,查看更多安裝和升級細節,進入幫助文檔

新特性

以下是最顯著的新特性:

  • 使用.NET Standard 2.0目標框架:這使得EF Core 2.0可支持多種.NET平台實現和應用程序類型,查看平台支持列表

  • LINQ解析改進:EF Core 2.0中的查詢更加高效,適應多種場景。舉個例子,增加了翻譯成SQL語句模式的數量限制,避免了在以前版本中因為客戶端計算導致多重查詢的問題。(優化了客戶端計算性能)

  • Like查詢支持:LINQ查詢可以使用EF.Functions.Like(),最終解析為SQL中的like語句,在必要的時候會進行內存計算,舉個例子,下面的查詢:

    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計划中


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM