Entity Framework Core 2.0 新特性

前言

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方法带有内存中的实现,当对内存中的数据进行查询时,或者在客户端需要发生相关的内存查询时,可以方便很多.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏一个番茄说

让你在WebView中用JS调Native Object

之所做这个东西,源于之前项目中需要把一些页面用webView来呈现,但是web中需要调用native的方法,比如获取本地存的某些数据、调用摄像头等等,这里也就是...

793
来自专栏灯塔大数据

干货 | Python 性能优化的20条招数

使用python时,你是不是需要性能优化?今天灯塔给你带来python性能优化的20条招数,记得收藏哟!

752
来自专栏LinXunFeng的专栏

打造Moya便捷解析库,提供RxSwift拓展

1、相信大家在使用Swift开发时,Moya是首选的网络工具,在模型解析这一块,Swift版模型解析的相关第三方库有很多,本人最习惯用的就是SwiftyJSON...

19911
来自专栏技术小讲堂

iBatis.Net(5):Data Map(了解)

总算,总算,能写点示例啦,呵呵,其实前面的几篇,我感觉自己写的也很生硬,没有Demo理解起来是很困难,很多名词,反正我初次接触iBatis的时候,是一点也不理解...

3186
来自专栏技术博客

Entity Framework CodeFirst尝试

Code First模式我们称之为“代码优先”模式,是从EF4.1开始新建加入的功能。使用Code First模式进行EF开发时开发人员只需要编写对应的数据类(...

753
来自专栏7号代码

Android网络与数据存储——SQLite

Android提供了一个管理数据库的工具类SQLiteOpenHelper,用于管理数据库的创建和版本更新,创建SQLiteOpenHelper的子类,并实现它...

662
来自专栏Java3y

Struts2【开发Action】

前言 前面Struts博文基本把Struts的配置信息讲解完了…..本博文主要讲解Struts对数据的处理 Action开发的三种方式 在第一次我们写开发步骤的...

3406
来自专栏Java 技术分享

Struts2 之 modelDriven & prepare 拦截器详解

3497
来自专栏技术博客

Entity Framework 关系约束配置

简单的说一下自己的理解,大家应该都很明白ADO.NET,也就是原生态的数据库操作,直接通过拼接SQL语句,表与表之间通过链接(inner join  left ...

591
来自专栏轮子工厂

设计模式(一) | 啥是工厂模式和策略模式?

511

扫码关注云+社区