0

我经常发现自己写这样的查询:

var voyages = db.VoyageRequests.Include("Carrier")
              .Where(u => (fromDate.HasValue ? u.date >= fromDate.Value : true) &&
                    (toDate.HasValue ? u.date <= toDate.Value : true) &&
                    u.Carrier != null &&
                    u.status == (int)VoyageStatus.State.InProgress)
              .OrderBy(u => u.date);

            return voyages;

在 where 语句中使用条件:

fromDate.HasValue ? u.date >= fromDate.Value : true

我知道另一种方法是:

 var voyages = db.VoyageRequests.Include("Carrier").Where(u => u.Carrier != null &&
                            u.status == (int)VoyageStatus.State.InProgress);

            if (fromDate.HasValue)
            {
                voyages = voyages.Where(u => u.date >= fromDate.Value);
            }

            if (toDate.HasValue)
            {
                voyages = voyages.Where(u => u.date <= toDate.Value);
            }

            return voyages.OrderBy(u => u.date);

当这两种方法转换为 SQL 表达式时,是否有任何可能影响性能的真正差异?

4

2 回答 2

2

第二个查询将创建更简单的 SQL,因为评估fromDate.HasValue发生toDate.HasValue在客户端上。在第一个查询中,三元运算符作为 SQL 查询的一部分在数据库服务器上进行评估。两者fromDatetoDate都将作为常量传输到服务器,而在第二个查询中只有 if .HasValueis true

我们谈论的是 SQL 语句的长度增加了几个字节,我不认为三元组的服务器端评估对查询性能有任何显着影响。

我会选择您认为更具可读性的内容。我个人会决定第二个查询。

于 2013-06-27T16:35:59.593 回答
1

If you want the simple SQL and the more readable C# you can create an Extension as suggested by Viktor Mitev http://mentormate.com/blog/improving-linq-to-entities-queries/

public static class WhereExtensions
{
   // Where extension for filters of any nullable type
   public static IQueryable<TSource> Where<Tsource, TFilter>
              (
                 this IQueryable <TSource> source, 
                 Nullable <TFilter> filter, 
                 Expression<Func<TSource, bool>> predicate
              ) where TFilter : struct
   {
       if (filter.HasValue)
       {
           source = source.Where(predicate);
       }

       return source;
   }

    // Where extension for string filters
   public static IQueryable<TSource> Where<TSource>
             (
                this IQueryable<TSource> source,   
                string filter, 
                Expression<Func<TSource, bool>> predicate
             )
   {
       if (!string.IsNullOrWhiteSpace(filter))
       {
           source = source.Where(predicate);
       }

       return source;
   }

    // Where extension for collection filters
   public static IQueryable<TSource> Where<TSource, TFilter>
             (
                this IQueryable<TSource> source, 
                IEnumerable<TFilter> filter, 
                Expression<Func<TSource, bool>> predicate
             )
 {
     if (filter != null && filter.Any())
     {
         source = source.Where(predicate);
     }

     return source;
 }

Then your "secound query" will look like this:

var voyages = db.VoyageRequests.Include("Carrier")
.Where(u => u.Carrier != null && u.status == (int)VoyageStatus.State.InProgress)
    .Where(u => u.date >= fromDate)
    .Where(u => u.date <= toDate)
    .OrderBy(u => u.date);

I don't know if it is more readable or if it will be confusing for some developers because it is more difficult to read directly form the code what part of the filtering is in use.

Maybe it will be more readable if you name the extensions function something like WhereIfFilterNotNull (or something meaningful :-)

var voyages = db.VoyageRequests.Include("Carrier")
.Where(u => u.Carrier != null && u.status == (int)VoyageStatus.State.InProgress)
    .WhereIfFilterNotNull(u => u.date >= fromDate)
    .WhereIfFilterNotNull(u => u.date <= toDate)
    .OrderBy(u => u.date);
于 2013-08-12T14:34:25.240 回答