c# - 使用 LINQ 进行查询,其中上下文与对象列表中的多个参数相匹配

标签 c# sql-server linq asp.net-core

假设我有一个包含多列的表“文章”,例如:

CREATE TABLE dbo.Article (
  Id int NOT NULL,
  ProducerId INT NOT NULL,
  Barcode nvarchar(50) NOT NULL,
  DescriptionText nvarchar(50) NOT NULL,
  ActiveFlag BIT NOT NULL
)

在我的 ASP.NET Core 应用程序中,我使用 LINQ 来查询该表,例如:

IQueryable<Article> query = _context.Article
                     .Where( p => p.Active == true );

这当然有效。 现在我得到了一些参数,这是一个非常简单的对象,带有 List < ArticleQuery >IEnumerable < ArticleQuery >

ArticleQuery.cs:

public class ArticleQuery
{
    [Required]
    public IEnumerable<ArticleRequest> Parts { get; set; }
}

public class ArticleRequest
{
    public int ProducerId { get; set; }
    public string Barcode { get; set; }
}

实际上我不知道如何将其集成到 LINQ 中。我尝试了很多东西,但最终我从未想到要得到这样的连接:

IQueryable<Article> query = _context.Article
    .Join(articleQuery.ArticleRequest,
        x => new { a = x.Barcode, b = x.Barcode},
        y => new { a = y.ProducerId, b = y.ProducerId},
        (x, y) => x);

这个伪代码也不起作用(但加入似乎是更好的尝试):

IQueryable<Article> query = _context.Article.Where( p => 
   p.Active == true &&
   articleQuery.ArticleRequest.ProducerId.Contains(p.ProducerId) && 
   articleQuery.ArticleRequest.Barcode.Contains(p.Barcode)
);

有什么想法可以正确地工作吗?

最佳答案

不幸的是,正常的 LINQ to Entities 代码无法很好地处理这种情况。

您想要使用的语法如下所示:

IQueryable<Article> query = _context.Article
    .Where( p => 
       p.Active == true &&
       articleQuery.ArticleRequest.Any(
           r => r.ProducerId == p.ProducerId && 
                r.ArticleRequest.Barcode == p.Barcode
       )
    );

遗憾的是,LINQ to Entities 无法像这样将内存中集合混合到数据库查询中。

理论上,您可以将您的 ArticleRequest 表示为数据库中的模型,并执行与上面类似的查询,但基于 IQueryable<ArticleRequest>来自您的上下文而不仅仅是参数变量。但这将是一个非常糟糕的解决方法。

有些人诉诸于具体化整套 Article对象,以便他们可以使用 LINQ to Objects 提供程序:

IQueryable<Article> query = _context.Article
    .AsEnumerable() // DANGER: materializes *all* the articles
    .Where( p => 
       p.Active == true &&
       articleQuery.ArticleRequest.Any(
           r => r.ProducerId == p.ProducerId && 
                r.ArticleRequest.Barcode == p.Barcode
       )
    );

但是当您获得更多文章时,这种方法将无法很好地扩展。

另一种方法是动态构建表达式树,这将产生如下所示的表达式:

    p => 
       p.Active == true &&
       ((p.ProducerId == 12 && p.BarCode == "12345") ||
        (p.ProducerId == 13 && p.BarCode == "54321") ||
        ...
       )

但是动态生成这种语句所需的代码非常丑陋并且很难理解。

更好的解决方案可能是使用一堆 Union。一种简洁的表示方法是:

IQueryable<Article> query = articleQuery.ArticleRequest.Aggregate(
    _context.Article.Where(p => false),
    (q, r) => q.Union(context.Article.Where(p => 
           r.ProducerId == p.ProducerId && 
           r.ArticleRequest.Barcode == p.Barcode))
)
    .Where(p => p.Active == true);

这将生成一个 SQL 查询,该查询将多个一次性查询的结果相互结合起来。使用更多的 C# 代码,您可以删除查询中多余的“Where False”部分。

还有其他库,例如 Dynamic LINQLINQKit它们旨在帮助解决此类问题,但自从我使用它们以来已经很长时间了,我无法保证它们在 Entity Framework 的现代版本中的工作效果如何。

或者,您可以完全避免使用 LINQ,而只为该查询构造一些自定义 SQL。这可能就是我会做的。只是要小心防止该条形码上的 SQL 注入(inject)!

关于c# - 使用 LINQ 进行查询,其中上下文与对象列表中的多个参数相匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45556081/

相关文章:

c# - Visual Studio - 如何重命名元素

c# - Entity Framework Core - 首先将动态值分配给代码中的公共(public)列

sql-server - 如何进行 `group by` 部分匹配

java - 从java调用过程时出现"The statement did not return a result set"

c# - 创建用于使用 C# 对版本号进行排序的自定义排序方法?

C# linq 如何搜索两个日期之间的数据

c# - 使用 .NET 4.0 编译器编译 .NET 3.5 项目

c# - Web API 2 在无效日期时间上抛出异常

mysql - SQL - 将一个表中多个列中的两行匹配到另一个表中的 1 行。

c# - 获取给定 ID 列表中每个 ID 的最新更新记录。 LINQ