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 条评论
登录 后参与评论

相关文章

来自专栏java一日一条

序列化和反序列化漏洞的简单理解

便于保存在内存、文件、数据库中;反序列化即逆过程,由字节流还原成对象。Java中的ObjectOutputStream类的writeObject()方法可以实现...

852
来自专栏技术博客

一步一步学Linq to sql(二):DataContext与实体

 DataContext类型(数据上下文)是System.Data.Linq命名空间下的重要类型,用于把查询句法翻译成SQL语句,以及把数据从数据库返回给调用方...

762
来自专栏GreenLeaves

关于EF Code First模式不同建模方式对建表产生的影响

今天在学EF Code First模式的时候,发现几个很有趣的问题,问题如下: 1、当编写玩实体后,不指定任何主键约束,EF会找长的最像Id的,然后设置其为主键...

1936
来自专栏GreenLeaves

EF 约定介绍

当前环境为EF Code First开发模式中 一、EF默认约定 1、常用约定 (1)、当没有显示指定实体主键的时候,EF会默认将长得最像Id的属性(且类型为G...

18810
来自专栏祝威廉

ElasticSearch Aggregations GroupBy 实现源码分析

也就是按newtype 字段进行group by,然后对num求平均值。在我们实际的业务系统中,这种统计需求也是最多的。

1373
来自专栏程序员的SOD蜜

使用OQL“语言”构造ORM实体类的复杂查询条件

OQL”语言“ 是PDF.NET数据开发框架的实体对象查询语言,一直以来,ORM的复杂查询条件都是困扰ORM的问题,所以很多时候不得不舍弃ORM,直接手工拼接S...

1796
来自专栏Android知识点总结

SpringBoot-08-之统一化json输出与自定义异常捕获

1071
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(89)-EF执行SQL语句与存储过程

EF上下文 DbContext包含了DataBase属性,里面有很多方法,但是实际我们只需要用到个方法

730
来自专栏双方都胜多负少

Flink 类型和序列化机制简介

Flink 内部实现了名为 TypeExtractror 的类,可以利用方法签名、子类信息等蛛丝马迹,自动提取和恢复类型信息(当然也可以显式声明,即本文所介绍的...

830
来自专栏DOTNET

Entity Framework——常见报错总结

1 实体属性配置为IsRequired()对更新的影响 抛出异常类型DbEntityValidationException 表结构: ? 实体: public ...

2907

扫码关注云+社区