使用LINQ to SQL时如何简化复杂的条件子句

时间:2012-11-21 12:53:19

标签: c# linq linq-to-sql refactoring

具有大量AND / OR标准的复杂语句很难阅读并容易出错 - 在正常的IF语句中,我可能会使用方法调用来简化is语句 - 例如:

        if (((user == myUser || user == yourUser) && user != Admin)
            && Something > SomethingElse
            && (thresholdDate > item.itemDate || (item.itemDate == null && item.itemType == itemIsDated))
            )
        {
            DoStuff();
        }

我可以重构用户和日期部分,以便于阅读:

        if (
            UserValid(user)
            && Something > SomethingElse
            && DateIsValid(thresholdDate, item)
           )
        {
            DoStuff();
        }

在LINQ查询中我可以做些什么来简化嵌套的IF?

例如,如果我有以下内容:

        var someResults = DataManager.Things
                                        .Where(item => (item.UserName == currentUser.UserName
                                                        || item.ParentUsername == currentUser.UserName)
                                                       && (item.ItemType == (int) ItemType.MyType
                                                           || item.ItemType == (int) ItemType.YourType)
                                                       && item.Result == null
                                                       && (
                                                              (item.Status == null
                                                               && (item.ItemDate < thresholdDate
                                                                   || item.ItemType == (int) ItemType.YourType)
                                                              )
                                                              ||
                                                              (item.Status != null &&
                                                               item.Status != "Rejected")
                                                          )
            )

**不是实际代码 - 只是一个简化的通用示例。

我希望能够将部分逻辑提取到方法中 - 或者以某种其他方式分离出AND / OR混乱,以便清楚地知道发生了什么。

我尝试在'item'中添加一个方法来为一些逻辑执行一种IsValidType(typeOptions)方法 - 这个编译很好但是LINQ抱怨它在运行时不能识别该方法。

我可以在项目上使用属性 - 但是我无法传递任何上下文信息(这使得它的用途有限)

如何使这种查询可读?

3 个答案:

答案 0 :(得分:0)

正如Tim在评论中提到的,你可以在大多数LINQ-to-something实现中使用方法。 结果如下:

.Where(item => CheckUserName(item)
    && CheckItemType(item)
    && CheckItemResult(item)
    && CheckItemStatus(item));

对于LINQ to SQL,LINQ to Entities或其他远程执行的实现,您至少可以利用查询优化并重写所有&&来分隔Where个调用,因为它们是等效的:

.Where(item => item.UserName == currentUser.UserName
    || item.ParentUsername == currentUser.UserName)
.Where(item => item.ItemType == (int) ItemType.MyType
    || item.ItemType == (int) ItemType.YourType)
.Where(item => item.Result == null)
.Where(item => (item.Status == null
    && (item.ItemDate < thresholdDate
        || item.ItemType == (int) ItemType.YourType))
    || (item.Status != null
        && item.Status != "Rejected"));

生成的查询将where子句加入一个枚举器。

答案 1 :(得分:0)

我假设您不能直接在代码中执行此操作。当然,如果你有很多类似的条件,你可以动态地为它们生成表达式。但你可以做很少的单一大的Where条件这里不会伤害性能=(。

如果你认为它更适合,你可以将你的单个大条件分成几个随后的Where()调用。转换为SQL时LINQ树将合并条件并生成单个选择。 DBMS还可以优化生成的SQL以实现最高效率。因此,在某些情况下,您可以有意识地编写未优化的查询以获得更好的可读性并依赖于自动优化。虽然要小心,并检查您的特定条件是否真的按照您的预期进行合并和优化。

如果可以将此代码移动到SQL存储过程,则可以配置LINQ to SQL以在需要时调用此过程。这将简化调用代码,但将复杂性移至SQL。我想你甚至可以将一些条件提取到SQL函数中,并在Where子句中使用它们进行调用。虽然老实说我从未使用它 - 只是阅读可能性。有关详细信息和示例,请查看以下文章:http://weblogs.asp.net/zeeshanhirani/archive/2008/05/21/table-valued-functions-in-linq-to-sql.aspx

答案 2 :(得分:0)

三种截然不同的选择:

  • 使用Resharper。它包含许多代码检查和重构,以减少if语句或指出冗余谓词。
  • 在linq-to-sql中,您可以跳过null的大部分检查。这是因为表达式被转换为SQL,而在SQL中没有空引用异常。当没有where a.Name == "x"时,像false这样的linq(to sql)子句只会产生a。在linq-to-objects中,这将抛出空引用异常。
  • 对于经常 的比较,您可以考虑将computed columns添加到数据库表中。就像评估IsRejected的列Status = 'Rejected'一样。在linq中将减少到where !item.IsRejected