1. 程式人生 > >Entity Framework Core 2.0 新特性

Entity Framework Core 2.0 新特性

order 3.4 高速緩存 字符串插值 常見 repr asp 而不是 spa

前言

Entity Framework Core 2.0更新也已經有一段時間了,園子裏也有不少的文章..

看了下2.0的新特性基本算是完成了我之前發布的路線圖的內容 很不錯

下面就介紹一下新特性.(本文的英文原文地址:這裏)

1.實體方面的新內容

1.1表拆分

現在可以將多個實體類型映射到將要共享主鍵列的同一個表,並且每一行將對應於兩個或多個實體。

使用表拆分識別關系(其中外鍵屬性形成主鍵)必須在共享表的所有實體類型之間進行配置:

modelBuilder.Entity<Product>()
    .HasOne(e => e.Details).WithOne(e => e.Product)
    .HasForeignKey
<ProductDetails>(e => e.Id); modelBuilder.Entity<Product>().ToTable("Products"); modelBuilder.Entity<ProductDetails>().ToTable("Products");

1.2所屬類型

擁有的實體類型可以與另一個擁有相同的實體類型共享CLR類型,但是由於CLR類型不能被識別,所以必須從另一個實體類型導航到它。包含定義導航的實體是所有者。當查詢所有者時,默認情況下將包含所有類型。

按照慣例,將為所屬類型創建一個影子主鍵,並通過使用表分割將其映射到與所有者相同的表。

使用所屬類型與EF6中使用復雜類型類似,(PS:這裏解釋一下EF6中的復雜類型,復雜類型是允許在實體中組織標量屬性的實體類型的非標量屬性。像實體一樣,復雜類型由標量屬性或其他復雜類型屬性組成。)

modelBuilder.Entity<Order>().OwnsOne(p => p.OrderDetails, cb =>
    {
        cb.OwnsOne(c => c.BillingAddress);
        cb.OwnsOne(c => c.ShippingAddress);
    });

public class Order
{
    
public int Id { get; set; } public OrderDetails OrderDetails { get; set; } } public class OrderDetails { public StreetAddress BillingAddress { get; set; } public StreetAddress ShippingAddress { get; set; } } public class StreetAddress { public string Street { get; set; } public string City { get; set; } }

1.3實體層(模型級)的查詢過濾器

此功能允許在元數據模型(一般在OnModelCreating)中直接在實體類型上定義LINQ查詢條件(通常傳遞給LINQ Where查詢運算符的布爾表達式)。這些過濾器自動應用於涉及這些實體類型的任何LINQ查詢,包括間接引用的實體類型,例如通過使用Include或直接導航屬性引用。

嗯..軟刪除,多租戶的數據庫設計 可以大量的使用這方面的功能,會減少很多代碼量

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
    //多租戶
    public int TenantId { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>().HasQueryFilter(
            p => !p.IsDeleted
            && p.TenantId == this.TenantId );
    }
}

1.4數據庫標量函數映射

這是一個很有用的功能,我們知道,我們的數據庫一般有很多自帶的數據庫函數,或者我們會定義一些標量的函數.

通過這個特性 我們可以很方便的在linq中調用這些函數,並通過linq翻譯成SQL

代碼如下:

public class BloggingContext : DbContext
{
    [DbFunction]
    public static int PostReadCount(int blogId)
    {
        //這裏不需要實現
        throw new Exception();
    }
}

然後直接就可以在linq查詢中使用了 如下:

var query =
    from p in context.Posts
    where BloggingContext.PostReadCount(p.Id) > 5
    select p;

值得註意的是:

  • 在生成SQL時,該方法的名稱將用作函數的名稱(在本例中為用戶定義的函數),但在方法註冊期間可以覆蓋名稱和模式
  • 目前只支持標量功能
  • 必須自行在數據庫中創建映射函數,EF Core遷移不會對其進行創建

2.性能提升方面

2.1DbContext連接池

在ASP.NET Core程序中我們使用EF Core一般都是將自定義DbContext類型註冊到依賴註入系統中,然後通過控制器中的構造函數參數獲取該類型的實例。意味著為每個請求創建一個新的DbContext實例。

所以在版本2.0中,我們引入了一種在依賴註入中註冊自定義DbContext類型的新方式,它透明地引入了一個可重用的DbContext實例池。要使用DbContext pooling,請在服務註冊期間使用AddDbContextPool代替AddDbContext

如下:

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

如果使用連接池,則在控制器請求DbContext實例時,將首先檢查池中是否有可用的實例。一旦請求處理完成,實例上的任何狀態都將重置,並且實例本身返回到池中。

這在思想概念上類似於ADO.NET中連接池的運作方式,並且能節省DbContext實例初始化成本。

2.2顯式編譯查詢

這是一個可選的性能功能,主要是為了在大規模場景中提供優勢。

顯式編譯的查詢API已經在以前版本的EF和LINQ to SQL中可用,以允許應用程序緩存查詢的翻譯,以便它們只能被計算一次並執行多次。

雖然EF Core通常可以根據查詢表達式的散列表示自動編譯和緩存查詢,但這種機制可以通過繞過哈希計算和高速緩存查找來獲得小的性能增益,從而允許應用程序使用已經通過調用委托編譯了查詢。

代碼如下:

// 創建一個顯示編譯的查詢
private static Func<CustomerContext, int, Customer> _customerById =
    EF.CompileQuery((CustomerContext db, int id) =>
        db.Customers
            .Include(c => c.Address)
            .Single(c => c.Id == id));

// 引用並使用它
using (var db = new CustomerContext())
{
   var customer = _customerById(db, 147);
}

3.查詢方面

3.1改進LINQ翻譯

使更多的查詢成功執行,並將更多的邏輯生成SQL讓它在數據庫中執行(而不是內存中),並且從數據庫中檢索更少的不必要的數據。

3.2GroupJoin改進

此工作改進了為組連接生成的SQL。

3.3FromSql和ExecuteSqlCommand中的字符串插值

C#6(C#6.0特性請移步:這裏)中引入了字符串插值,這是一個允許C#表達式直接嵌入到字符串文字中的功能,提供了一種在運行時構建字符串的好方法。

在EF核2.0,我們增加了對插值字符串中的特殊支持,我們接受原始的SQL字符串兩個主要的API:FromSqlExecuteSqlCommand

這種新的支持允許以“安全”的方式使用C#字符串插值。這樣就可以防止在運行時動態構建SQL時發生的常見SQL註入攻擊.

如下:

var city = "London";
var contactTitle = "Sales Representative";

using (var context = CreateContext())
{
    context.Set<Customer>()
        .FromSql($@"
            SELECT *
            FROM ""Customers""
            WHERE ""City"" = {city} AND
            ""ContactTitle"" = {contactTitle}")
            .ToArray();
  }

會生成如下參數化的SQL語句:

@p0=London (Size = 4000)
@p1=Sales Representative (Size = 4000)

SELECT *
FROM ""Customers""
WHERE ""City"" = @p0
    AND ""ContactTitle"" = @p1

3.4EF.Functions.Like()

添加了EF.Functions屬性(註意,這裏應該是可以擴展的,添加更多的數據庫方法),EF Core可以使用它們來定義映射到數據庫函數或操作符的方法,以便可以在LINQ查詢中調用它們。這樣一個方法的第一個例子是Like():

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

值得註意的是,Like方法帶有內存中的實現,當對內存中的數據進行查詢時,或者在客戶端需要發生相關的內存查詢時,可以方便很多.

Entity Framework Core 2.0 新特性