首页 > 解决方案 > ASP.NET Core 3 EF Framework - 运行大量查询时出现瞬时故障

问题描述

我正在将 EF Core 用于 ASP.NET 网站。在一种方法中,我有大约 7 个类似于(简单示例)的复杂查询:

var query1 = context.Playarea
            .Include(x => x.Cats)
            .Where(x => x.Cats.Any())
            .SelectMany(x => x.Cats.Select(y => new MyClass(x.Id, y.Name, y.Age))).ToList();


var query2 = context.Playarea
            .Include( x => x.Dogs)
            .Where(x => x.Dogs.Any())
            .SelectMany(x => x.Dogs, (x, y) => new MyClass(x.Id, y.Name, y.Age)).ToList();


var query3 = context.Playarea
            .Include( x => x.Dogs)
            .Include( x => x.Leads)
            .Where(x => x.Dogs.Any())
            .SelectMany(x => x.Dogs, (x, y) => new MyClass(x.Id, y.Leads.Name, y.Age)).ToList();

var query4 = context.Playarea
            .Include( x => x.Birds)
            .Where(x => x.Birds.Any())
            .SelectMany(x => x.Birds, (x, y) => new MyClass(x.Id, y.Name, y.Age)).ToList();

return query1.Concat(query2).Concat(query3).Concat(query4).ToList();

这通常有效,但有时页面会崩溃:

An unhandled exception was thrown by the application. System.InvalidOperationException: An exception has been raised that is likely due to a transient failure. Consider enabling transient error resiliency by adding 'EnableRetryOnFailure()' to the 'UseSqlServer' call.
 ---> Microsoft.Data.SqlClient.SqlException (0x80131904): A transport-level error has occurred when receiving results from the server. (provider: TCP Provider, error: 0 - The semaphore timeout period has expired.)
 ---> System.ComponentModel.Win32Exception (121): The semaphore timeout period has expired.

我知道我可以添加“EnableRetryOnFailure()”,但我担心真正的原因是它同时运行多个查询。有没有办法让这个更安全、更高效?

我已经尝试查找指南,但是如果您一次尝试大量查询,它们都没有涵盖该怎么做。

标签: entity-frameworkasp.net-core-3.0

解决方案


老实说,我认为这可能与您的代码关系不大,而与某种网络或数据库问题有关。虽然有一些方法可以改进此代码,但它确实没有理由不应该始终如一地工作。

也就是说,您正在阻止所有这些查询,这绝不是一个好主意。EF Core 所做的一切都是异步的。同步方法仅阻塞异步方法,并且仅存在于无法使用异步的场景中,例如桌面应用程序中的事件委托等。简而言之,您应该始终使用异步方法,除非您遇到无法使用的特定情况。在 ASP.NET Core 应用程序中,不存在这种情况,因此应始终使用 async。长短,使用ToListAsync代替ToListawait每一行。

接下来,您的 where 子句没有意义。例如,无论您只在有狗的项目上选择很多,还是在所有项目上选择很多,无论它们是否有狗,您仍然会得到相同的结果。无论哪种方式,所有这些都将在数据库中运行,因此一种方法或另一种方法甚至没有性能优势。Include如果您从关系中选择,您也不需要使用。EF 足够聪明,可以根据它需要返回的数据发出连接。

var query1 = await context.Playarea
    .SelectMany(x => x.Cats.Select(y => new MyClass(x.Id, y.Name, y.Age))).ToListAsync();

还有你的两个狗查询的问题。两个查询将返回相同的结果。唯一的区别是一组将具有y.Name,而另一组将具有y.Leads.Name作为Name值。包含的行为Leads不会以某种方式过滤掉没有的结果,Leads当然,第一个查询根本没有过滤,无论哪种方式。我想你会想要类似下面的东西:

var query3 = await context.Playarea
        .SelectMany(x => x.Dogs, (x, y) => new MyClass(x.Id, y.Leads is null ? y.Name : y.Leads.Name, y.Age)).ToListAsync();

换句话说,Leads.Name如果关系存在,将使用,Name否则将回退。


推荐阅读