您的位置:首页 > 编程语言 > ASP

(25)ASP.NET Core EF查询(复杂查询运算符、原生SQL查询、异步查询)

2019-11-13 16:35 2695 查看

1.复杂查询运算符

在生产场景中,我们经常用到LINQ运算符进行查询获取数据,现在我们就来了解下生产场景经常出现几种复杂查询运算符。

1.1联接(INNER JOIN)

借助LINQ Join运算符,可根据每个源的键选择器连接两个数据源,并在键匹配时生成值的元组。

var query = from blog in _context.Set<Blog>()
join post in _context.Set<Post>()
on blog.BlogId equals post.BlogId
select new { blog, post };

SQL:

SELECT [blog].[BlogId], [blog].[Createtime], [blog].[Updatetime], [blog].

1.2左联接(Left Join)

虽然Left Join不是LINQ运算符,但关系数据库具有常用于查询的Left Join的概念。LINQ查询中的特定模式提供与服务器上的LEFT JOIN相同的结果。

var query = from blog in _context.Set<Blog>()
join post in _context.Set<Post>()
on blog.BlogId equals post.BlogId into grouping
from post in grouping.DefaultIfEmpty()
select new { blog, post };

SQL:

SELECT [blog].[BlogId], [blog].[Createtime], [blog].[Updatetime], [blog].[Url], [post].[PostId], [post].[BlogId], [post].[Content], [post].[Title]
FROM [Blog] AS [blog]
LEFT JOIN [Post] AS [post] ON [blog].[BlogId] = [post].[BlogId]

SQL Server Profiler:

1.3分组(GroupBy)

LINQ GroupBy运算符创建IGrouping<TKey, TElement>类型的结果,其中TKey和TElement可以是任意类型。此外,IGrouping实现了IEnumerable<TElement>,这意味着可在分组后使用任意LINQ运算符来对其进行组合。

var query = from blog in _context.Set<Blog>()
group blog by blog.Url into g
select new
{
g.Key,
Count = g.Count()
};

SQL:

SELECT [blog].[Url] AS [Key], COUNT(*) AS [Count]
FROM [Blog] AS [blog]
GROUP BY [blog].[Url]

SQL Server Profiler:


分组的聚合运算符出现在Where或OrderBy(或其他排序方式)LINQ运算符中。它在SQL中将Having子句用于Where子句。

var query = from blog in _context.Set<Blog>()
group blog by blog.Url into g
where g.Count() > 0
orderby g.Key
select new
{
g.Key,
Count = g.Count()
};

SQL:

SELECT [blog].[Url] AS [Key], COUNT(*) AS [Count]
FROM [Blog] AS [blog]
GROUP BY [blog].[Url]
HAVING COUNT(*) > 0
ORDER BY [Key]

SQL Server Profiler:

EF Core支持的聚合运算符如下所示:
●Avg
●Count
●LongCount
●Max
●Min
●Sum

1.4SelectMany

借助LINQ SelectMany运算符,可为每个外部元素枚举集合选择器,并从每个数据源生成值的元组。

var query0 = from b in _context.Set<Blog>()
from p in _context.Set<Post>()
select new { b, p };

var query1 = from b in _context.Set<Blog>()
from p in _context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()
select new { b, p };

var query2 = from b in _context.Set<Blog>()
from p in _context.Set<Post>().Select(p => b.Url + "=>" + p.Title).DefaultIfEmpty()
select new { b, p };

SQL:

SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url], 

.[PostId], [p].[BlogId], [p].[Content], [p].[Title] FROM [Blog] AS [b] CROSS JOIN [Post] AS [p] SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url], [t0].[PostId], [t0].[BlogId], [t0].[Content], [t0].[Title] FROM [Blog] AS [b] CROSS APPLY ( SELECT [t].[PostId], [t].[BlogId], [t].[Content], [t].[Title] FROM ( SELECT NULL AS [empty] ) AS [empty] LEFT JOIN ( SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title] FROM [Post] AS [p] WHERE [b].[BlogId] = [p].[BlogId] ) AS [t] ON 1 = 1 ) AS [t0] SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url], [t0].[c] FROM [Blog] AS [b] CROSS APPLY ( SELECT [t].[c] FROM ( SELECT NULL AS [empty] ) AS [empty] LEFT JOIN ( SELECT ([b].[Url] + N'=>') + [p].[Title] AS [c] FROM [Post] AS [p] ) AS [t] ON 1 = 1 ) AS [t0]

[p]SQL Server Profiler:


好了,这里就不多写关于LINQ其他示例了,如果需要了解的小伙伴们,可以移步“
101个LINQ示例" target=_blank>, [post].[PostId], [post].[BlogId], [post].[Content], [post].[Title] FROM [Blog] AS [blog] INNER JOIN [Post] AS [post] ON [blog].[BlogId] = [post].[BlogId][/code]

SQL Server Profiler:

1.2左联接(Left Join)

虽然Left Join不是LINQ运算符,但关系数据库具有常用于查询的Left Join的概念。LINQ查询中的特定模式提供与服务器上的LEFT JOIN相同的结果。

var query = from blog in _context.Set<Blog>()
join post in _context.Set<Post>()
on blog.BlogId equals post.BlogId into grouping
from post in grouping.DefaultIfEmpty()
select new { blog, post };

SQL:

SELECT [blog].[BlogId], [blog].[Createtime], [blog].[Updatetime], [blog].[Url], [post].[PostId], [post].[BlogId], [post].[Content], [post].[Title]
FROM [Blog] AS [blog]
LEFT JOIN [Post] AS [post] ON [blog].[BlogId] = [post].[BlogId]

SQL Server Profiler:

1.3分组(GroupBy)

LINQ GroupBy运算符创建IGrouping<TKey, TElement>类型的结果,其中TKey和TElement可以是任意类型。此外,IGrouping实现了IEnumerable<TElement>,这意味着可在分组后使用任意LINQ运算符来对其进行组合。

var query = from blog in _context.Set<Blog>()
group blog by blog.Url into g
select new
{
g.Key,
Count = g.Count()
};

SQL:

SELECT [blog].[Url] AS [Key], COUNT(*) AS [Count]
FROM [Blog] AS [blog]
GROUP BY [blog].[Url]

SQL Server Profiler:


分组的聚合运算符出现在Where或OrderBy(或其他排序方式)LINQ运算符中。它在SQL中将Having子句用于Where子句。

var query = from blog in _context.Set<Blog>()
group blog by blog.Url into g
where g.Count() > 0
orderby g.Key
select new
{
g.Key,
Count = g.Count()
};

SQL:

SELECT [blog].[Url] AS [Key], COUNT(*) AS [Count]
FROM [Blog] AS [blog]
GROUP BY [blog].[Url]
HAVING COUNT(*) > 0
ORDER BY [Key]

SQL Server Profiler:

EF Core支持的聚合运算符如下所示:
●Avg
●Count
●LongCount
●Max
●Min
●Sum

1.4SelectMany

借助LINQ SelectMany运算符,可为每个外部元素枚举集合选择器,并从每个数据源生成值的元组。

var query0 = from b in _context.Set<Blog>()
from p in _context.Set<Post>()
select new { b, p };

var query1 = from b in _context.Set<Blog>()
from p in _context.Set<Post>().Where(p => b.BlogId == p.BlogId).DefaultIfEmpty()
select new { b, p };

var query2 = from b in _context.Set<Blog>()
from p in _context.Set<Post>().Select(p => b.Url + "=>" + p.Title).DefaultIfEmpty()
select new { b, p };

SQL:

SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url], 

.[PostId], [p].[BlogId], [p].[Content], [p].[Title] FROM [Blog] AS [b] CROSS JOIN [Post] AS [p] SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url], [t0].[PostId], [t0].[BlogId], [t0].[Content], [t0].[Title] FROM [Blog] AS [b] CROSS APPLY ( SELECT [t].[PostId], [t].[BlogId], [t].[Content], [t].[Title] FROM ( SELECT NULL AS [empty] ) AS [empty] LEFT JOIN ( SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title] FROM [Post] AS [p] WHERE [b].[BlogId] = [p].[BlogId] ) AS [t] ON 1 = 1 ) AS [t0] SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].[Url], [t0].[c] FROM [Blog] AS [b] CROSS APPLY ( SELECT [t].[c] FROM ( SELECT NULL AS [empty] ) AS [empty] LEFT JOIN ( SELECT ([b].[Url] + N'=>') + [p].[Title] AS [c] FROM [Post] AS [p] ) AS [t] ON 1 = 1 ) AS [t0]

[p]SQL Server Profiler:


好了,这里就不多写关于LINQ其他示例了,如果需要了解的小伙伴们,可以移步“[url=https://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b]101个LINQ示例
”这里了解。

2.原生SQL查询

有一些复杂业务场景,使用LINQ查询可能会导致SQL查询效率低下并不适用,那么这时候就需要到原生SQL查询了。EF Core为我们提供FromSql扩展方法基于原始SQL查询。 FromSql只能在直接位于DbSet<>上的查询根上使用。

var blogs = _context.Blog.FromSql("SELECT * FROM dbo.Blog").ToList();

原生SQL查询可用于执行存储过程。

var blogs = _context.Blog
.FromSql("EXECUTE dbo.GetMostPopularBlogs")
.ToList();

2.1原始SQL查询使用参数化

向原始SQL查询引入任何用户提供的值时,必须注意防范SQL注入攻击。除了验证确保此类值不包含无效字符,还要将值与SQL文本参数化处理。
下面的示例通过在SQL查询字符串中包含形参占位符并提供额外的实参,将单个形参传递到存储过程。虽然此语法可能看上去像String.Format语法,但提供的值包装在DbParameter中,且生成的参数名称插入到指定{0}占位符的位置。

var url = "http://blogs.msdn.com/webdev";
var blogs = _context.Blog
.FromSql("EXECUTE dbo.GetMostPopularBlogForUrl {0}", url)
.ToList();

SQL:

exec sp_executesql N'EXECUTE dbo.GetMostPopularBlogForUrl @p0
',N'@p0 nvarchar(4000)',@p0=N'http://blogs.msdn.com/webdev'

SQL Server Profiler:


还可以构造DbParameter并将其作为参数值提供。由于使用了常规SQL参数占位符而不是字符串占位符,因此可安全地使用FromSql:

var urlParams = new SqlParameter("Url", "http://blogs.msdn.com/webdev");
var blogs = _context.Blog
.FromSql("EXECUTE dbo.GetMostPopularBlogForUrl @Url", urlParams)
.ToList();

SQL:

exec sp_executesql N'EXECUTE dbo.GetMostPopularBlogForUrl @Url
',N'@Url nvarchar(28)',@Url=N'http://blogs.msdn.com/webdev'

SQL Server Profiler:

2.2使用LINQ编写SQL

可使用LINQ运算符在初始的原始SQL查询基础上进行组合。EF Core将其视为子查询,并在数据库中对其进行组合。下面的示例使用原始SQL查询,该查询从表值函数 (TVF) 中进行选择。然后,使用LINQ进行筛选和排序,从而对其进行组合。

var searchTerm = "http://blogs.msdn.com/visualstudio";
var blogs = _context.Blog
.FromSql($"SELECT * FROM dbo.Blog")
.Where(b => b.Url == searchTerm)
.Include(c=>c.Post)
.OrderByDescending(b => b.Createtime)
.ToList();

SQL:

exec sp_executesql N'SELECT [b].[BlogId], [b].[Createtime], [b].[Updatetime], [b].

3.异步查询

当在数据库中执行查询时,异步查询可避免阻止线程。异步查询对于在客户端应用程序中保持响应式UI非常重要。 异步查询还可以增加Web应用程序中的吞吐量,即通过释放线程,以处理其他Web应用程序中的请求。

public async Task<IActionResult> Index()
{
var id1 = Thread.CurrentThread.ManagedThreadId.ToString();

var blogs = await _context.Blog.ToListAsync();

var id2 = Thread.CurrentThread.ManagedThreadId.ToString();

return View(blogs);
}

当我们运行以上代码时候,通过在关键字await上下文加入两段获取线程ID代码,我们会看到如下结果:

看到两段线程代码输出ID结果没有?从上图可以观察到,当我们在进入某个视图或者方法时候,执行到await某一个方法,当前线程不会一直等待下去,会立马回收到线程池,供其他地方调用!当该await方法返回数据时候,才从线程池调用空闲线程执行await方法下文余下的步骤。所以UI界面才不会进入假死状态。

参考文献:
[url=https://docs.microsoft.com/zh-cn/ef/core/querying/complex-query-operators]复杂查询运算符" target=_blank> FROM ( SELECT * FROM dbo.Blog ) AS [b] WHERE [b].[Url] = @__searchTerm_1 ORDER BY [b].[Createtime] DESC, [b].[BlogId]',N'@__searchTerm_1 nvarchar(4000)',@__searchTerm_1=N'http://blogs.msdn.com/visualstudio' exec sp_executesql N'SELECT [b.Post].[PostId], [b.Post].[BlogId], [b.Post].[Content], [b.Post].[Title] FROM [Post] AS [b.Post] INNER JOIN ( SELECT [b0].[BlogId], [b0].[Createtime] FROM ( SELECT * FROM dbo.Blog ) AS [b0] WHERE [b0].[Url] = @__searchTerm_1 ) AS [t] ON [b.Post].[BlogId] = [t].[BlogId] ORDER BY [t].[Createtime] DESC, [t].[BlogId]',N'@__searchTerm_1 nvarchar(4000)',@__searchTerm_1=N'http://blogs.msdn.com/visualstudio'[/code]

SQL Server Profiler:

3.异步查询

当在数据库中执行查询时,异步查询可避免阻止线程。异步查询对于在客户端应用程序中保持响应式UI非常重要。 异步查询还可以增加Web应用程序中的吞吐量,即通过释放线程,以处理其他Web应用程序中的请求。

public async Task<IActionResult> Index()
{
var id1 = Thread.CurrentThread.ManagedThreadId.ToString();

var blogs = await _context.Blog.ToListAsync();

var id2 = Thread.CurrentThread.ManagedThreadId.ToString();

return View(blogs);
}

当我们运行以上代码时候,通过在关键字await上下文加入两段获取线程ID代码,我们会看到如下结果:

看到两段线程代码输出ID结果没有?从上图可以观察到,当我们在进入某个视图或者方法时候,执行到await某一个方法,当前线程不会一直等待下去,会立马回收到线程池,供其他地方调用!当该await方法返回数据时候,才从线程池调用空闲线程执行await方法下文余下的步骤。所以UI界面才不会进入假死状态。

参考文献:
[url=https://docs.microsoft.com/zh-cn/ef/core/querying/complex-query-operators]复杂查询运算符

[url=https://docs.microsoft.com/zh-cn/ef/core/querying/raw-sql]原生SQL查询
异步查询

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐