假设我有一个包含多列的表“文章”,例如:
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 LINQ和 LINQKit它们旨在帮助解决此类问题,但自从我使用它们以来已经很长时间了,我无法保证它们在 Entity Framework 的现代版本中的工作效果如何。
或者,您可以完全避免使用 LINQ,而只为该查询构造一些自定义 SQL。这可能就是我会做的。只是要小心防止该条形码上的 SQL 注入(inject)!
关于c# - 使用 LINQ 进行查询,其中上下文与对象列表中的多个参数相匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45556081/