我正在开发一个接待域应用程序(.Net Core2.2),其中我正在开发一个报告模块。在仪表板上,很少有过滤器可以从数据库中获取记录。
下面是我用来包含过滤器的DTO
public class SearchDto
{
public DateTime DateForm {get;set;}
public DateTime DateTo {get;set;}
public DateSearchType SearchType {get;set;}
public string RegionId {get;set;}
public OrdersStatus status {get;set;}
public string PaymentModeTypes {get;set;}
public string channel {get;set;}
}
这里,DateSearchType是一个具有值的枚举
还有OrdersStatus (枚举),它的值包括All、确认、取消、PaymnetFailed等。
PaymentModeTypes可以是用于ex的单个字符串或逗号分隔字符串:"NetBanking,CreditCard,DebitCard,Cash“
RegionId也是一个单独的字符串或逗号分隔字符串,作为"101,102,102“。
同样适用于"Web“或"Web,Mobile”频道
我现在使用的核心表达方式如下
var v = Database.Orders.List(
x => ((SearchType == DateSearchType.Start) ? x.Services.Any(y => (y.MinStartTime >= DateForm.Date && y.MinStartTime <= DateTo.Date)) || DateForm.Date.Equals(DateTime.MinValue) : true)
&& ((SearchType == DateSearchType.End) ? x.Services.Any(y => y.MaxEndTime >= DateForm.Date && y.MaxEndTime <= DateTo.Date) : true)
&& ((SearchType == DateSearchType.Creation) ? x.BookingDate.Date >= DateForm.Date && x.BookingDate.Date <= DateTo.Date : true)
&& (RegionId.Length == 0 || RegionId.Contains(x.RegionId))
&& (status == OrdersStatus.All || x.Status == status)
&& (string.IsNullOrEmpty(PaymentModeTypes) || PaymentTypes.Contains(x.PaymentType))
&& (string.IsNullOrEmpty(channel) || channels.Contains(x.ChannelName)), //Where
x => x.Guests,
x => x.Services
);
这里,来宾和服务是Orders模型中另两个作为导航属性设置的表
这个表达式工作得很好,但是执行时间太长了,有什么好的方法来优化它,或者用正确的方法重写这段代码?
另外,如果没有提供过滤器的值,那么排除任何过滤器的最佳实践是什么。
很少有过滤器不是强制性的,它们可以提供也可以不提供,所以查询执行必须是动态的。如果未提供筛选值,则当前实现
&& (string.IsNullOrEmpty(PaymentModeTypes) || PaymentTypes.Contains(x.PaymentType))
有人能建议一些好的材料或任何代码关于这一点,所以我可以优化它。
请忽略台风,因为我不习惯使用黑暗主题
编辑1:客户端评估设置为off
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}
发布于 2020-05-16 23:55:54
生成的SQL中存在一些效率低下的问题,这可能会导致性能问题。
在expected之前,需要有条件地链接多个Where
调用,或者使用一些谓词生成器实用程序来构建条件性Where
谓词,只有必要条件。
这在EF Core中通常不需要,因为它试图自动消除这种情况。它对逻辑表达式(||
,&&
)这样做,但对条件表达式(? :
)却没有这样做。因此,解决方案是用等效的逻辑表达式替换后者。
在大多数情况下,您都是这样做的,但对于前3种情况则不是这样。
x => ((SearchType == DateSearchType.Start) ? x.Services.Any(y => (y.MinStartTime >= DateForm.Date && y.MinStartTime <= DateTo.Date)) || DateForm.Date.Equals(DateTime.MinValue) : true)
&& ((SearchType == DateSearchType.End) ? x.Services.Any(y => y.MaxEndTime >= DateForm.Date && y.MaxEndTime <= DateTo.Date) : true)
&& ((SearchType == DateSearchType.Creation) ? x.BookingDate.Date >= DateForm.Date && x.BookingDate.Date <= DateTo.Date : true)
使用
x => (SearchType != DateSearchType.Start || x.Services.Any(y => y.MinStartTime >= DateForm.Date && y.MinStartTime <= DateTo.Date) || DateForm.Date.Equals(DateTime.MinValue))
&& (SearchType != DateSearchType.End || x.Services.Any(y => y.MaxEndTime >= DateForm.Date && y.MaxEndTime <= DateTo.Date))
&& (SearchType != DateSearchType.Creation || x.BookingDate.Date >= DateForm.Date && x.BookingDate.Date <= DateTo.Date)
看看这是否有帮助。当然,生成的SQL将是最优的。
注意到您对逗号分隔字符串和Contains
过滤器使用不同的变量名。所以我假设你有这样的东西
public IEnumerable<string> PaymentTypes => (PaymentModeTypes ?? "").Split(", ");
public IEnumerable<string> channels => (channel ?? "").Split(", ");
这是很好的,并将在必要时生成SQL IN (...)
条件。
您可以考虑对区域执行相同的操作,例如添加
public IEnumerable<string> RegionIds => (RegionId ?? "").Split(", ");
并替换
&& (RegionId.Length == 0 || RegionId.Contains(x.RegionId))
使用
&& (string.IsNullOrEmpty(RegionId) || RegionIds.Contains(x.RegionId))
https://stackoverflow.com/questions/61833776
复制相似问题