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

C# 使用LINQ进行查询,其中上下文匹配对象列表中的多个参数,c#,sql-server,linq,asp.net-core,C#,Sql Server,Linq,Asp.net Core,假设我有一个包含多列的表“Article”,例如: 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核心应用程序中,我使用LINQ查询该表,例如: IQueryable<

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

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核心应用程序中,我使用LINQ查询该表,例如:

IQueryable<Article> query = _context.Article
                     .Where( p => p.Active == true );
IQueryable查询=\u context.Article
其中(p=>p.Active==true);
这当然有效。 现在我得到了一些参数,它是一个非常简单的对象,有一个列表或者一个IEnumerable

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; }
}
公共类ArticleQuery
{
[必需]
公共IEnumerable部分{get;set;}
}
公共类请求
{
public int ProducerId{get;set;}
公共字符串条形码{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查询=\u context.Article
.Join(articleQuery.ArticleRequest,
x=>新的{a=x.Barcode,b=x.Barcode},
y=>new{a=y.ProducerId,b=y.ProducerId},
(x,y)=>x);
此外,此伪代码也不起作用(但Join似乎是更好的尝试):

IQueryable查询=\u context.Article.Where(p=>
p、 活动==真&&
articleQuery.ArticleRequest.ProducerId.Contains(p.ProducerId)和
articleQuery.ArticleRequest.Barcode.Contains(p.Barcode)
);

你知道如何正确工作吗?

不幸的是,这是一种普通LINQ到实体代码无法很好处理的情况

您希望使用的语法如下所示:

IQueryable<Article> query = _context.Article
    .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") ||
        ...
       )
但是,随着文章的增多,这种方法无法很好地扩展

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

IQueryable<Article> query = _context.Article
    .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") ||
        ...
       )
但动态生成此类语句所需的代码非常难看,也很难理解

一个更好的解决方案可能是使用一组工会。一种简洁的表达方式是:

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);
IQueryable和IQueryable是为解决这类问题而设计的,但我已经很久没有使用它们了,我无法保证它们在对抗Entity Framework的现代版本时会有多好


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

我自己解决了这个问题。虽然不太完美,但很有效

我使用“RequestArticles”表为我的Web服务创建了一个新数据库:

我把我的请求写进了那张表:

Guid requestGuid = Guid.NewGuid();
foreach (var requestArticle in requestedArticles.Articles) requestArticle.RequestId = requestGuid;
_context.RequestArticle.AddRange(requestedArticles.Articles); //IEnumerable list of articles
_context.SaveChanges();
在那之后,我只是手动加入我的新请求表

IQueryable<Articles> query = _context.Articles.FromSql<Articles>(
    @"SELECT      a.*, ra.Barcode AS RequestedBarcode
      FROM        dbo.Articles a
      INNER JOIN  Webservice.dbo.RequestArticle ra
              ON  ra.ProducerId = a.ProducerId AND ra.Barcode = a.Barcode
      WHERE       ra.RequestId = {1}",
    requestGuid);
IQueryable查询=\u context.Articles.FromSql(
@选择一个*的ra条形码作为请求的条形码
来自dbo.a.的文章
内部连接Webservice.dbo.RequestArticle ra
在ra.ProducerId=a.ProducerId和ra.Barcode=a.Barcode上
其中ra.RequestId={1}“,
请求GUID);

目前,我很难用一个与新上下文不同的字符串替换SQL字符串中的新静态数据库名称,但基本操作非常快。尽管这不是我希望的解决方案,因为我认为LINQ+EF比这更聪明。:-)

非常感谢您非常详细的回答!在我尝试使用较少的数据进行查询后,查询语法起了作用,因为它之前运行到了超时。这种查询似乎太慢了。在内存中存储我的文章的“黑客解决方案”是行不通的,因为里面有2亿条条目。我想我会将这些文章查询插入我的数据库,然后在数据库中加入这些内容。这不是我所希望的,但它似乎是直截了当的。是的,当你处理数亿行时,你常常不得不牺牲简单的代码来获得可接受的性能。祝你好运!现在我尝试动态创建表达式树。这很有魅力,但只要我查询到100多个条形码/生产者组合,我的代码就会从300毫秒的响应时间变为超时。我猜表达式树在某个地方定义了最大大小。生成的SQL有点难看,但应该可以工作:
exec sp_executesql N'SELECT[t].[ProducerId],[t].[Barcode],[t].[DescriptionText]FROM(从[Article]中选择TOP(@)作为[p0],其中[p0].[ActiveFlag]为NULL且[ProducerId]为=@@@u请求的物品\u ProducerId\u 0)和([p0].[Barcode]=@@@u Barcode\u 1)或([p0].[ProducerId]=@@@u请求的物品\u ProducerId\u 2)和([p0].[Barcode]=@@@u Barcode\u 3)))或([p0].[ProducerId]=@@@u请求的物品\u ProducerId\u 10)和(……。)))……
@Stix:如果您的应用程序需要无限数量的查询项,那么我肯定会使用表联接方法。如果您不希望用户这样做,那么我会加入一个保护语句来阻止超过合理数量的查询。
IQueryable<Articles> query = _context.Articles.FromSql<Articles>(
    @"SELECT      a.*, ra.Barcode AS RequestedBarcode
      FROM        dbo.Articles a
      INNER JOIN  Webservice.dbo.RequestArticle ra
              ON  ra.ProducerId = a.ProducerId AND ra.Barcode = a.Barcode
      WHERE       ra.RequestId = {1}",
    requestGuid);