简体   繁体   English

使用LINQ进行实体动态联接

[英]Using LINQ to Entities dynamic JOIN

So say you have a table of Keywords. 假设您有一个关键字表。 For simplicity sake lets just say it has 2 fields an Id integer, Keyword varchar(100). 为简单起见,我们只说它有2个字段,一个ID整数,关键字varchar(100)。 A query comes in for multiple keywords. 查询输入多个关键字。 For example a query for "quick brown fox". 例如,查询“快速的棕色狐狸”。 The requirement would be that we will take any records where the Id selected contains at least once occurrence of all three keywords. 要求是,我们将记录所有ID至少包含一次出现的所有三个关键字的所有记录。 Moreover, it can be a partial match using the StartsWith. 此外,使用StartsWith可以部分匹配。 I can use the PredicateBuilder to build the multiple OR clauses that will ultimately be needed but to also filter these records, I would need to execute a JOIN on the same table for each keyword. 我可以使用PredicateBuilder来构建最终将需要的多个OR子句,但还要过滤这些记录,我需要为每个关键字在同一表上执行JOIN。 I should note that the Id field is not unique and can have multiple entries. 我应该注意,Id字段不是唯一的,可以有多个条目。

The SQL Looks more or less like this or should SQL看起来或多或少像这样或应该

select k1.Id
  from Keywords k1
 inner join Keywords k2 on k1.Id = k2.Id
 inner join Keywords k3 on k2.Id = k3.Id
 where k1.Keyword like @k1
   and k2.Keyword like @k2
   and k3.Keyword like @k3

The LINQ that I have so far would be 我到目前为止拥有的LINQ将是

var predicate = PredicateBuilder.False<Keyword>();
foreach (string term in searchTerms)
{
   string temp = term;
   predicate = predicate.Or(p => p.Keyword.StartsWith(temp));
}
var keys = Keywords.AsExpandable().Where(predicate).ToList();

This will produce SQL that more or less looks like: 这将产生或多或少看起来像的SQL:

SELECT 
[Extent1].[Id] AS [Id]
FROM  [dbo].[Keywords] AS [Extent1]
WHERE ([Extent1].[Keyword] LIKE @p__linq__1 ESCAPE '~') OR ([Extent1].[Keyword] LIKE @p__linq__2 ESCAPE '~') OR ([Extent1].[Keyword] LIKE @p__linq__3 ESCAPE '~')

In order to use this result I would then have to do a DistinctBy and then JOIN back to my results. 为了使用此结果,我将不得不做一个DistinctBy,然后再加入我的结果。 This can produce a huge memory requirement and I am trying to find a solution that does most of what I want on the server. 这可能会产生巨大的内存需求,我正在尝试找到一种解决方案,该解决方案可以满足我在服务器上的大部分需求。

To rephrase your query, you're looking for the ID values such that a group of all of the keywords for that ID contains all of the search terms that you're looking for. 要改写您的查询,您要查找ID值,以便该ID的所有关键字组包含您要查找的所有搜索词。

Rather than trying to use Join to create a group of all of the keywords for an ID, you can use GroupBy , and then when you have the group of all keywords sharing an ID it's simple enough to filter out only the ones in which the group contains all of the search terms. 您可以使用GroupBy ,而不是尝试使用Join为ID创建所有关键字的组,然后,当您拥有共享ID的所有关键字的组时,仅过滤掉该组中的关键字就足够简单了。包含所有搜索词。

var query = keywords.GroupBy(keyword => keyword.Id)
    .Where(group => searchTerms.All(term => 
        group.Any(keyword => keyword.Keyword.StartsWith(term)))
    .Select(group => group.Key);

Ultimately I was able to get it to produce the SQL I was looking for. 最终,我能够得到它来生成我想要的SQL。 Even though the likelyhood that this issue is unique to me, I will post my solution. 即使这个问题可能是我独有的,我仍会发布解决方案。 Perhaps it will be useful to someone. 也许对某人有用。

LINQ LINQ

var term = searchTerms[0];
var keys = Keywords.Where (k => k.keyword.StartsWith(term));
for(var i=1; i < searchTerms.Length; i++)
{
    var searchTerm = searchTerms[i];
    keys = 
        from k1 in keys
        join k2 in Keywords on k1.Id equals k2.Id
        where k2.keyword.StartsWith(searchTerm)
        select k1;
}

PRODUCED SQL: 产生的SQL:

-- Region Parameters
DECLARE @p__linq__0 VarChar(1000) = 'the%'
DECLARE @p__linq__1 VarChar(1000) = 'brown%'
DECLARE @p__linq__2 VarChar(1000) = 'fox%'
-- EndRegion
SELECT 
    [Extent1].[Id] AS [Id]
    FROM    [dbo].[Keywords] AS [Extent1]
    INNER JOIN [dbo].[Keywords] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]
    INNER JOIN [dbo].[Keywords] AS [Extent3] ON [Extent1].[Id] = [Extent3].[Id]
    WHERE ([Extent1].[keyword] LIKE @p__linq__0 ESCAPE '~') AND ([Extent2].[keyword] LIKE @p__linq__1 ESCAPE '~') AND ([Extent3].[keyword] LIKE @p__linq__2 ESCAPE '~')

Of course I would put some checking to make sure that I have more than 1 searchTerm before beginning my loop. 当然,在开始循环之前,我会进行一些检查以确保我拥有多个searchTerm。 But you get the picture. 但是你明白了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM