代码之家  ›  专栏  ›  技术社区  ›  mr.coffee

LinqKit谓词或with Contains的计算结果等于

  •  2
  • mr.coffee  · 技术社区  · 7 年前

    我正在实现对我的应用程序的搜索。除非用户的搜索是通过搜索相关实体级联的,否则搜索是有效的。我已经调试了代码并测试了实体框架生成的SQL。我发现问题是 Contains() 当它应该是“LIKE”时,在SQL中转换为“=”。 包含() 在FirstName、MiddleName等的初始谓词中起作用,但在 if (cascade) 代码块。

    public IList<Individual> Find(string search, bool cascade, bool includeInactive)
    {
        _context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
        IQueryable<Individual> query = _context.Individuals;
        if (!string.IsNullOrWhiteSpace(search))
        {
            search = search.Trim();
            string[] searchParts = search.Split(' ');
            ExpressionStarter<Individual> predicate = PredicateBuilder.New<Individual>(false);
            foreach (string searchPart in searchParts)
            {
                predicate = predicate.Or(c =>
                    c.FirstName.Contains(searchPart) || c.MiddleNames.Contains(searchPart) ||
                    c.LastName.Contains(searchPart) || c.PreferredName.Contains(searchPart));
                if (cascade)
                {
                    predicate = predicate.Or(c =>
                        c.IndividualOrganisationGroups.Select(og => og.OrganisationGroup).Select(g => g.Group.Name)
                            .Contains(searchPart));
                }
            }
            query = query.Where(predicate);
        }
        if (!includeInactive)
        {
            query = query.Where(c => c.Active);
        }
        return query.ToList();
    }
    

    从EF生成的SQL:

    SELECT 
        [Extent1].[ID] AS [ID], 
        [Extent1].[RegistrationTypeID] AS [RegistrationTypeID], 
        [Extent1].[Title] AS [Title], 
        [Extent1].[FirstName] AS [FirstName], 
        [Extent1].[MiddleNames] AS [MiddleNames], 
        [Extent1].[LastName] AS [LastName], 
        [Extent1].[PreferredName] AS [PreferredName], 
        [Extent1].[RegistrationNumber] AS [RegistrationNumber], 
        [Extent1].[Username] AS [Username], 
        [Extent1].[AzureID] AS [AzureID], 
        [Extent1].[Notes] AS [Notes], 
        [Extent1].[Active] AS [Active], 
        [Extent1].[CreatedDate] AS [CreatedDate], 
        [Extent1].[CreatedBy] AS [CreatedBy], 
        [Extent1].[UpdatedDate] AS [UpdatedDate], 
        [Extent1].[UpdatedBy] AS [UpdatedBy]
        FROM [dbo].[Individual] AS [Extent1]
        WHERE ([Extent1].[FirstName] LIKE @p__linq__0 ESCAPE '~') OR ([Extent1].[MiddleNames] LIKE @p__linq__1 ESCAPE '~') OR ([Extent1].[LastName] LIKE @p__linq__2 ESCAPE '~') OR ([Extent1].[PreferredName] LIKE @p__linq__3 ESCAPE '~') OR ( EXISTS (SELECT 
            1 AS [C1]
            FROM   [dbo].[IndividualOrganisationGroup] AS [Extent2]
            INNER JOIN [dbo].[OrganisationGroup] AS [Extent3] ON [Extent2].[OrganisationGroupID] = [Extent3].[ID]
            INNER JOIN [dbo].[Group] AS [Extent4] ON [Extent3].[GroupID] = [Extent4].[ID]
            WHERE ([Extent1].[ID] = [Extent2].[IndividualID]) AND (([Extent4].[Name] = @p__linq__4) OR (1 = 0))
        ))
    -- p__linq__0: '%screen%' (Type = AnsiString, Size = 8000)
    
    -- p__linq__1: '%screen%' (Type = AnsiString, Size = 8000)
    
    -- p__linq__2: '%screen%' (Type = AnsiString, Size = 8000)
    
    -- p__linq__3: '%screen%' (Type = AnsiString, Size = 8000)
    
    -- p__linq__4: 'screen' (Type = AnsiString, Size = 8000)
    

    有问题的SQL(最后一个WHERE子句):

    WHERE ([Extent1].[ID] = [Extent2].[IndividualID]) AND (([Extent4].[Name] = @p__linq__4) OR (1 = 0))
    

    它应该是什么样子:

    WHERE ([Extent1].[ID] = [Extent2].[IndividualID]) AND (([Extent4].[Name] LIKE @p__linq__4) OR (1 = 0))
    

    所以我的问题是,如何将这些代码翻译成实体框架SQL中的LIKE?

    predicate = predicate.Or(c =>
                        c.IndividualOrganisationGroups.Select(og => og.OrganisationGroup).Select(g => g.Group.Name)
                            .Contains(searchPart));
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Ivan Stoev    7 年前

    它与LINQKit谓词生成器无关。原因是

    c.IndividualOrganisationGroups.Select(og => og.OrganisationGroup).Select(g => g.Group.Name)
    

    是 IEnumerable<string> IQueryable<string> ),所以你的目标是 Enumerable Queryable ) Contains 方法而不是 string.Contains 就像其他地方一样。

    而不是 Select 包含 ,你真正需要的是 Any 扩展方法。

    示例中最简洁的语法是:

    c => c.IndividualOrganisationGroups
        .Any(og => og.OrganisationGroup.Group.Name.Contains(searchPart))
    

    当然,如果您需要检查 Group (甚至没有),然后使用 + Any :

    c => c.IndividualOrganisationGroups
        .Select(og => og.OrganisationGroup.Group)
        .Any(g => g.Name.Contains(searchPart))
    

    或者最自然的方法,通过以“正常”的方式编写查询,将带有过滤器的集合转换为单bool。例如 , Where 等,并将参数化 任何

    c => c.IndividualOrganisationGroups
        .Select(og => og.OrganisationGroup.Group)
        .Where(g => g.Name.Contains(searchPart))
        .Any())