Entity Framework Core 2.0 新特性

weixin_33816946發表於2017-10-25
前言

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方法帶有記憶體中的實現,當對記憶體中的資料進行查詢時,或者在客戶端需要發生相關的記憶體查詢時,可以方便很多.

 

相關文章