简化具有多个条件的复杂 linq 查询
本文关键字:复杂 linq 查询 条件 | 更新日期: 2023-09-27 18:36:47
最近我遇到了修复错误的代码,如下所示。只是很难理解它,并使更改看起来也有风险。有没有简单的方法来分解这样的查询,即德摩根定律或以不同的方式重写。
SomeReturnType xyz()
{
return (from m in this.context.tblMessages
where m.SystemActionID.HasValue &&
(userID == null || m.RecipientID == null || m.RecipientID == userID) &&
m.TargetUserType == userType &&
(
(territoryID.HasValue && !m.TerritoryID.HasValue) ||
(!territoryID.HasValue && !m.TerritoryID.HasValue) ||
(territoryID.HasValue && m.TerritoryID.HasValue && territoryID.Value == m.TerritoryID.Value)
) &&
(
(regionID.HasValue && !m.RegionID.HasValue) ||
(!regionID.HasValue && !m.RegionID.HasValue) ||
(regionID.HasValue && m.RegionID.HasValue && regionID.Value == m.RegionID.Value)
) &&
(
(teamID.HasValue && !m.TeamID.HasValue) ||
(!teamID.HasValue && !m.TeamID.HasValue) ||
(teamID.HasValue && m.TeamID.HasValue && teamID.Value == m.TeamID.Value)
) &&
m.SystemActionData == additionalData &&
(!completed.HasValue || m.IsSystemActionCompleted == completed.Value)
select m).FirstOrDefault();
} //function xyz ends
首先,此代码看起来像是试图将错误的 SQL 代码移动到 LINQ:这些语句包含参数检查,以尝试创建可以具有"可选"参数的查询。看起来作者试图将存储过程移动到代码中。
这两件事都是非常糟糕的主意。
- 首先,SQL Server 将根据查询的参数缓存查询的执行计划。这意味着该过程的第一次执行决定了所有后续调用的计划。"可选"参数通常会导致非最佳计划(例如,通过省略索引)
- 存储过程是一个抽象层,它隐藏了复杂的查询,允许您使用产品的完整 SQL 语法。将它们移动到代码只会让事情变得更难,而不是更容易。
- 最后,LINQ 不需要"可选"参数。您可以编写查询,当您将 IQueryable 转换为 IEnumerable 时,将生成最终的 SQL 语句。在这种情况下,当您调用 FirstOrDefault 时。
例如,您可以更改以下内容:
(from m in this.context.tblMessages
where m.SystemActionID.HasValue &&
(userID == null || m.RecipientID == null || m.RecipientID == userID) &&
m.TargetUserType == userType &&
(
(territoryID.HasValue && !m.TerritoryID.HasValue) ||
(!territoryID.HasValue && !m.TerritoryID.HasValue) ||
(territoryID.HasValue && m.TerritoryID.HasValue && territoryID.Value == m.TerritoryID.Value)
) &&
对此
if (userId==null)
return null;
var query=from m in this.context.tblMessages
where m.SystemActionID != null
&& (m.RecipientID == null || m.RecipientID == userID)
&& m.TargetUserType == userType ;
if (territoryID.HasValue)
query=query.Where(m=>m.TerritoryId==null || m.TerritoryId==teritoryID);
您可以以相同的方式简化语句的其余部分,例如:
if (regionID.HasValue)
query=query.Where(m=>m.RegionId==null || m.RegionId==regionID);
if (teamID.HasValue)
query=query.Where(m=>m.TeamID==null || m.TeamID==teamID);
LINQ 查询不应将参数与常量进行比较 - 为什么要强制服务器执行可以在客户端上轻松执行的常量检查?
这将导致查询更简单,
在这种情况下,我正在使用IQueryable。我使用了一些虚拟名称而不是您的示例来演示想法本身 - 您需要逐步应用"where"子句。
IQueryable<Account> query1 = from account in storage.Accounts
where account.Username == username
select account;
IQueryable<SomeNewTypeIfNecessary> query2 = from account in query1
where account.ID > 100
select new SomeNewTypeIfNecessary { ID = account.ID };
// Final call doing real query to database.
List<SomeNewTypeIfNecessary> accounts = query2.ToList();
此外,这种技术允许动态添加所需的语句(主要是where和orderby子句)以获得依赖于某些用户选择的特定数据(例如排序方向)。