EF Core使用Linq进行数据查询。

基本查询

微软提供了一百多个示例来演示查询,地址:https://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b

我们可以通过下面的代码进行简单的查询:

//获取全部数据
var blogs = context.Blogs.ToList(); //获取单个实体
var blog = context.Blogs.Single(b => b.BlogId == 1); //筛选
var blogs = context.Blogs
.Where(b => b.Url.Contains("dotnet"))
.ToList();

加载关联数据

EF Core有三种常见模型来加载关联数据:

  • 预先加载:表示从数据库中加载关联数据,作为初始查询的一部分
  • 显式加载:表示稍后从数据库中显式加载关联数据
  • 延迟加载:表示在访问关联数据时,再从数据库中加载关联数据

预先加载

使用Include方法指定要包含在查询结果中的关联数据。例如:

using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.Include(blog => blog.Owner)
.ToList();
}

关联数据可以是有层级的,可通过链式调用ThenInclude,进一步包含更深级别的关联数据。:

using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ThenInclude(author => author.Photo)
.Include(blog => blog.Owner)
.ThenInclude(owner => owner.Photo)
.ToList();
}

如果更改查询,从而使其不再返回查询以之为开头的实体类型的实例,则会忽略 include 运算符。例如:

using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.Select(blog => new
{
Id = blog.BlogId,
Url = blog.Url
})
.ToList();
}

此时EF Core会忽略包含,并生成警告日志。

显式加载

通过 DbContext.Entry(...) API 显式加载导航属性。例如:

using (var context = new BloggingContext())
{
var blog = context.Blogs
.Single(b => b.BlogId == 1); context.Entry(blog)
.Collection(b => b.Posts)
.Load(); context.Entry(blog)
.Reference(b => b.Owner)
.Load();
}

延迟加载

使用延迟加载的最简单方式是通过安装 Microsoft.EntityFrameworkCore.Proxies 包,并通过调用 UseLazyLoadingProxies 来启用该包。 例如:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString);

或者在ServiceConfigure中,调用services.AddDbContext方法时启用:

services.AddDbContext<BloggingContext>(
b => b.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString));

EF Core 延迟加载需要属性必须具有是共有的,且具有virtual修饰符,只有这样才可以被子类重写。为何要这样做,可以参考我之前的文章《Castle DynamicProxy基本用法(AOP)》。

下面的代码演示了延迟加载的用法:

public class Blog
{
public int Id { get; set; }
public string Name { get; set; } public virtual ICollection<Post> Posts { get; set; }
} public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; } public virtual Blog Blog { get; set; }
}

此时EF Core会使用代理类进行延迟加载数据。

EF Core还提供了不使用代理的方式进行延迟加载,此方法需要向实体类中注入ILazyLoader实例,并通过该实例实现get访问:

public class Blog
{
private ICollection<Post> _posts; public Blog()
{
} private Blog(ILazyLoader lazyLoader)
{
LazyLoader = lazyLoader;
} private ILazyLoader LazyLoader { get; set; } public int Id { get; set; }
public string Name { get; set; } public ICollection<Post> Posts
{
get => LazyLoader.Load(this, ref _posts);
set => _posts = value;
}
}

此种方法需要注入ILazyLoader,从而造成更多的包依赖。

使用EF Core延迟加载,可能会造成循环引用,此时无法使用Json.Net进行序列化,需要对此进行一些配置:

public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
}

客户端 vs. 服务器

EF Core支持部分查询在客户端进行、部分查询发送到服务器,此种情况下可能会造成性能问题。

当发生客户端筛选数据的时候,EF Core会发出警告,也可以配置当发生客户端筛选时抛出异常:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}

跟踪和非跟踪

默认情况下,EF Core跟踪查询返回的实体,如果我们不需要跟踪查询返回的实体,则可以通过AsNoTracking方法禁用跟踪。

using (var context = new BloggingContext())
{
var blogs = context.Blogs
.AsNoTracking()
.ToList();
}

或者在DbContext级别禁用跟踪:

using (var context = new BloggingContext())
{
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; var blogs = context.Blogs.ToList();
}

当使用投影查询结果时,如果包含实体类型,则会对实体类型执行跟踪,例如下面的查询,将会对Blog和Post进行跟踪:

using (var context = new BloggingContext())
{
var blog = context.Blogs
.Select(b =>
new
{
Blog = b,
Posts = b.Posts.Count()
});
}

另外,如果查询结果中不包含任何实体类型,则不执行跟踪。例如:

using (var context = new BloggingContext())
{
var blog = context.Blogs
.Select(b =>
new
{
Id = b.BlogId,
Url = b.Url
});
}

原始SQL查询

当Linq无法满足查询需求,或因为使用Linq生成效率比较低的SQL查询时,可以考虑使用原始SQL进行查询。EF Core支持原始SQL语句和存储过程。

原始SQL语句:

var blogs = context.Blogs
.FromSql("SELECT * FROM dbo.Blogs")
.ToList();

存储过程:

var blogs = context.Blogs
.FromSql("EXECUTE dbo.GetMostPopularBlogs")
.ToList();

参数传递

当使用原始SQL进行查询时,必须使用参数化查询以抵御SQL注入攻击。

好的一点是,EF Core在设计时就替我们考虑了如何防御SQL注入攻击,因此当我们使用FromSql方法时,参数中如果有使用到拼接字符串的情况,则会自动为我们生成SQL查询参数,例如:

var user = "johndoe";

var blogs = context.Blogs
.FromSql($"EXECUTE dbo.GetMostPopularBlogsForUser {user}")
.ToList();

上面的SQL语句虽然看上去像是直接拼接的字符串,其实EF Core已经为我们生成了查询参数。

当然了,我们也可以手工创建查询参数:

var user = new SqlParameter("user", "johndoe");

var blogs = context.Blogs
.FromSql("EXECUTE dbo.GetMostPopularBlogsForUser @user", user)
.ToList();

当数据库的存储过程使用了命名参数时,手工创建查询参数将会派上用场:

var user = new SqlParameter("user", "johndoe");

var blogs = context.Blogs
.FromSql("EXECUTE dbo.GetMostPopularBlogs @filterByUser=@user", user)
.ToList();

拼接Linq

当我们使用原始SQL查询时,EF Core仍然支持我们使用linq编写查询语句。在执行查询时,EF Core会检查我们的sql语句是否支持拼接,如果支持的情况下,则会将linq过滤语句拼接为sql一并发送到数据库进行查询。

跟踪

原始SQL中的跟踪与Linq查询的跟踪方式一致。

关联数据

原始SQL中查询关联数据的方式与Linq查询的关联方式一致。

全局筛选器

全局筛选器对于软删除和多租户非常有用。定义方式如下:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Property<string>("TenantId").HasField("_tenantId"); // Configure entity filters
modelBuilder.Entity<Blog>().HasQueryFilter(b => EF.Property<string>(b, "TenantId") == _tenantId);
modelBuilder.Entity<Post>().HasQueryFilter(p => !p.IsDeleted);
}

我们可以在特定的查询中禁用全局筛选器:

blogs = db.Blogs
.Include(b => b.Posts)
.IgnoreQueryFilters()
.ToList();

03-EF Core笔记之查询数据的更多相关文章

  1. EntityFramework Core笔记:查询数据(3)

    1. 基本查询 1.1 加载全部数据 using System.Linq; using (var context = new LibingContext()) { var roles = contex ...

  2. 深入理解 EF Core:使用查询过滤器实现数据软删除

    原文:https://bit.ly/2Cy3J5f 作者:Jon P Smith 翻译:王亮 声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的.其中可能会去除一些本人实在不知道如何组织 ...

  3. EF Core 的关联查询

    0 前言 本文会列举出 EF Core 关联查询的方法: 在第一.二.三节中,介绍的是 EF Core 的基本能力,在实体中配置好关系,即可使用,且其使用方式,与编程思维吻合,是本文推荐的方式. 第四 ...

  4. EF Core利用Transaction对数据进行回滚保护

    What? 首先,说一下什么是EF Core中的Transaction Transaction允许以原子方式处理多个数据库操作,如果事务已提交,则所有操作都应用于数据库,如果事务回滚,则没有任何操作应 ...

  5. 02-EF Core笔记之保存数据

    EF Core通过ChangeTracker跟踪需要写入数据库的更改,当需要保存数据时,调用DbContext的SaveChanges方法完成保存. 基本的添加.更新.删除操作示例如下: using ...

  6. EF Core 使用编译查询提高性能

    今天,我将向您展示这些EF Core中一个很酷的功能,通过使用显式编译的查询,提高查询性能. 不过在介绍具体内容之前,需要说明一点,EF Core已经对表达式的编译使用了缓存:当您的代码需要重用以前执 ...

  7. EF core的原生SQL查询以及用EF core进行分页查询遇到的问题

    在用.net core进行数据库访问,需要处理一些比较复杂的查询,就不得不用原生的SQL查询了,然而EF Core 和EF6 的原生sql查询存在很大的差异. 在EF6中我们用SqlQuery和Exe ...

  8. Dapper, Ef core, Freesql 插入大量数据性能比较(一)

    需求:导入9999行数据时Dapper, Ef core, Freesql 谁的性能更优,是如何执行的,级联增加谁性能更佳. 确认方法:sql server 的 sys.dm_exec_query_s ...

  9. oracle系列笔记(1)---查询数据

    查询数据 1. 查询(select .. form ..)    (1)普通查询 select * from employees --代表查询employees表中所有数据 select last_n ...

随机推荐

  1. Obtaining the backtrace - libunwind

    Sometimes when working on a large project, I find it useful to figure out all the places from which ...

  2. Netflix 开源 Polynote:对标 Jupyter,一个笔记本运行多种语言

    谈到数据科学领域的开发工具,Jupyter 无疑是非常知名的一种.它具有灵活高效的特点,非常适合进行开发.调试.分享和教学.近日,Netflix(奈飞)居然也玩起了跨界,他们开源了一个名为 Polyn ...

  3. linux bash编程之函数和循环控制

    函数:实现独立功能的代码段 函数只有在调用时才会执行 语法一: function F_NAME{ 函数体 } 语法二: F_NAME() { 函数体 } 函数的返回值: 默认函数返回值:函数执行状态返 ...

  4. RPM命令执行失败:bash: rpm: 未找到命令...

    出现错误截图如下: 这是由于误操作导致rpm文件缺失导致 将另一台完好的服务器上RPM文件及缺失文件上传至异常服务器上即可修复 异常服务器A:192.168.1.230 完好服务器B: 任意 服务器B ...

  5. 原生线程池这么强大,Tomcat 为何还需扩展线程池?

    前言 Tomcat/Jetty 是目前比较流行的 Web 容器,两者接受请求之后都会转交给线程池处理,这样可以有效提高处理的能力与并发度.JDK 提高完整线程池实现,但是 Tomcat/Jetty 都 ...

  6. X86架构CPU常识(主频,外频,FSB,cpu位和字长,倍频系数,缓存,CPU扩展指令集,CPU内核和I/O工作电压,制造工艺,指令集,超流水线与超标量)

    1.主频 主频也叫时钟频率,单位是MHz,用来表示CPU的运算速度. CPU的主频=外频×倍频系数.很多人认为主频就决定着CPU的运行速度,这不仅是个片面的,而且对于服务器来讲,这个认识也出现了偏差. ...

  7. Python 代码块

    代码块 骏马金龙https://www.cnblogs.com/f-ck-need-u/p/9925021.html https://www.cnblogs.com/jin-xin/articles/ ...

  8. Stack Overflow上59万浏览量的提问:为什么会发生ArrayIndexOutOfBoundsException?

    在逛 Stack Overflow 的时候,发现了一些访问量像昆仑山一样高的问题,比如说这个:为什么会发生 ArrayIndexOutOfBoundsException?这样看似简单到不值得一问的问题 ...

  9. 关于layer的基本所有的事件全部失效问题

    只要在页面中,要是存在id="undefined", layer的基本所有的事件全部失效. <input type="radio" id="un ...

  10. 第五章 Unity中的基础光照(2)

    目录 1. Unity中的环境光和自发光 2. 在UnityShader中实现漫反射光照模型 2.1 实践:逐顶点光照 2.2 实践:逐像素光照 2.3 半兰伯特模型 1. Unity中的环境光和自发 ...