使用linq到实体的条件查询的最佳实践

时间:2013-06-27 16:03:50

标签: entity-framework entity-framework-4 linq-to-entities

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

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表达式时,是否存在可能影响性能的真正差异?

2 个答案:

答案 0 :(得分:2)

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

我们讨论的是SQL语句长度的几个字节,我不相信三元组的服务器端评估会对查询性能产生任何重大影响。

我会选择你觉得更具可读性的东西。我个人会决定第二个问题。

答案 1 :(得分:1)

如果您想要简单的SQL和更易读的C#,您可以按照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;
 }

然后你的“secound query”将如下所示:

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);

我不知道它是否更具可读性,或者它是否会让某些开发人员感到困惑,因为直接从代码中读取过滤器的哪个部分正在使用中更加困难。

如果你将扩展函数命名为WhereIfFilterNotNull(或者有意义的东西:-)

,也许它会更具可读性
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);