【问题标题】:LINQ and optional parametersLINQ 和可选参数
【发布时间】:2015-10-23 14:04:26
【问题描述】:

我正在设计一个由 MVC 应用程序使用的 Web 服务(非常简单的东西),但我需要在 Web 服务中使用一个方法,该方法最多需要四个可选参数(即 catId、brandId、lowestPrice 和最高价格) .

我如何编写 Linq 查询以使其实质上执行

databaseObject.Products.Where(p=> (p.Category == ANY if catId==null, else catId))

我希望这是有道理的。

【问题讨论】:

    标签: c# asp.net linq controllers


    【解决方案1】:

    方法的参数可以接受空值,并且可以为每个非空参数评估 Where 限制:

    IQueryable<Product> q = databaseObject.Products;
    
    if (catId != null)
    {
        q = q.Where(p => p.Category == catId);
    }
    if (brandId != null)
    {
        q = q.Where(p => p.Brand == brandId);
    }
    // etc. the other parameters
    
    var result = q.ToList();
    

    【讨论】:

    • 您可能应该将 q 声明为 IQueryable&lt;Product&gt;,但除此之外,这是完美的解决方案。
    • 这个方案不是说所有产品都是从服务器下载然后在客户端系统上过滤的吗?
    • @Kris Vandermotten 这不是必需的。
    • @Will Whitehead 如果 databaseObject.Products 是 IQueriable 则不正确。查询的具体化将在何时调用 ToArray()、ToList()、First()、Single()、Any() 或其他方法
    • @Kris Vandermotten 你说得对,q 需要明确声明,我已经更新了答案。
    【解决方案2】:

    如果这是 Linq To SQL:

    databaseObject.Products.Where(p=> (catId == null || p.Category == catId) );
    

    Linq To SQL,如果 CatId 为空,则有效地编写发送到后端的 SQL,而无需使用 where 子句。您可以拥有多个这样的构造,并且只有那些具有非空值的构造才会包含在 where 构造中。

    【讨论】:

      【解决方案3】:
      databaseObject.Products.Where(p=> ((catId==null) ||  (p.Category == catId)))
      

      对于您的其他 3 个可选参数,您可以将它们与它们相加,在一个 linq 语句中完成整个搜索。

      【讨论】:

      • CatId != null 是多余的,在那里不需要。如果它不为空,它将捷径到第一个。你是什​​么意思“如果 catid == null 在这种情况下,Linq 是一种过度杀伤力?Linq 会有效地创建一个没有 where 子句的 SQL)。
      • 这不是一个好主意。马丁的回答要好得多。无论 catId 是否为空,这将生成一个参数化查询,然后将其发送到数据库。然后,数据库将生成一个次优查询计划(或者更糟糕的是,为您提供的参数选择一个最佳查询计划,然后为所有未来的查询重用相同的查询计划)。 Martin 的回答将为它所提供的实际参数生成一个最佳查询计划。
      • @Robert - 您的评论和 Cetin 的评论似乎有冲突?
      • @Joe @CetinBasoz 不正确,至少在当前驱动程序中是这样。未来可能并非总是如此,但今天就是这样。这是一个真实的例子:int? catid=null; var result=Categories.Where(c=&gt;c.id==1 || catid==null); 产生以下 SQL:DECLARE @p__linq__0 Int = null SELECT [Extent1].[id] AS [id], [Extent1].[name] AS [name] FROM [Categories] AS [Extent1] WHERE [Extent1].[id] = 1 OR @p__linq__0 IS NULL
      • @Robert 在声明其他人不正确之前,请尝试阅读他们所说的内容,并尝试以正确的方式对他们所说的内容进行建模。从什么时候 Linq To SQL 吐出 eSQL ?我的回答显然是从“如果这是 Linq To SQL”开始的。
      【解决方案4】:

      以下内容应该可以解决问题:

      IEnumerable<Product> GetProducts(int? categoryId)
      {
          var result = databaseObject.Products.Where(product => ProductCategoryPredicate(product, categoryId)
      }
      
      /// <summary>
      /// Returns true if the passed in categoryId is NULL
      /// OR if it's not null, if the passed in categoryId matches that
      /// of the passed in product
      ///
      /// This method will get called once for each product returned from 
      /// databaseObject.Products</summary>
      bool ProductCategoryPredicate(Product product, int? categoryId)
      {
          if (categoryId.HasValue)
          {
              return product.Category == categoryId.Value;
          }
          else
          {
              return true;
          }
      }
      

      这可以/可以简化为单行 LINQ 语句(见下文),但为了清楚起见,我在上面写了长手:

      IEnumerable<Product> GetProducts(int? categoryId)
      {
          var result = databaseObject.Products.Where(product => !categoryId.HasValue || product.Category == categoryId.Value);
      }
      

      【讨论】:

      • 很好的答案,但肯定 linq 查询本质上是在说“SELECT product WHERE CategoryID IS NULL OR CategoryID = [param value]”?如果实际传入了参数值,我更希望“SELECT product WHERE CategoryID = [param value]”,或者如果没有传递参数,则只返回所有产品而不考虑其 categoryID。
      • 不完全是,被检查的categoryId是传入的那个。代码在功能上等同于SELECT * FROM products P WHERE ISNULL(@productId, P.CategoryId) = P.CategoryId。如果 CategoryId 参数为 NULL (!categoryId.HasValue),则第二个 GetProducts 方法中的谓词基本上返回 TRUE,如果计算结果为 false(即 categoryId 不为 NULL),则继续将 categoryId.Value 与 product.CategoryId 进行比较寻找匹配。
      • 您不会收到不知道如何将 ProductCategoryPredicate 转换为 SQL 的错误吗?
      猜你喜欢
      • 2012-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-14
      • 2019-12-15
      • 2011-09-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多