簡體   English   中英

如何檢查字符串是否包含實體框架中列表中的任何字符串?

[英]How do you check if a string contains any strings from a list in Entity Framework?

我正在嘗試搜索數據庫以查看字符串是否包含搜索詞列表的元素。

var searchTerms = new List<string> { "car", "232" };
var result = context.Data.Where(data => data.Name.Contains(searchTerms) ||
                                        data.Code.Contains(searchTerms));

如果 searchTerms 是一個字符串,這將起作用,但我一直試圖讓它與字符串列表一起使用。

本質上我需要 SQL 會說

SELECT * FROM Data
WHERE Name LIKE '%car%'
OR Name LIKE '%232%'
OR Code LIKE '%car%'
OR Code LIKE '%232%'

linq list contains any in list似乎是我能找到的最接近這種情況的東西。

Where(data => searchTerms.Contains(data.Name) || searchTerms.Contains(data.Code)僅將完全匹配項帶回搜索詞列表。

我也嘗試在實體框架中搜索多個關鍵字搜索,並且已經用盡了努力。 有什么方法可以實現我的目標嗎?

您可以嘗試使用Any方法,我不確定它是否受支持,但值得嘗試:

var result = context.Data.Where(data => searchTerms.Any(x => data.Name.Contains(x)) ||
                                        searchTerms.Any(x => data.Code.Contains(x));

如果這給您NotSupportedException您可以在Where之前添加AsEnumerable以獲取所有記錄並在內存而不是數據庫中執行查詢。

我遲到了,但是使用SearchExtensions nuget 包,您可以執行以下操作

var result = context.Data.Search(x => x.Name, x => x.Code).Containing(searchTerms);

這將構建一個表達式樹,因此仍將在服務器上(而不是在內存中)執行查詢,並且本質上將運行您想要的上述 SQL

以下是一個功能齊全的示例,說明如何使用多個關鍵字實現不同類型的搜索。

這個例子特別針對@Hamza Khanzada 關於Pomelo.EntityFrameworkCore.MySql類似問題

它執行類似於名為@NinjaNye 的庫的功能。


EqualsQuery()使用了最簡單的方法,它只是根據關鍵字的精確匹配來測試數據庫字段(盡管大小寫無關緊要)。 這是@Mohsen Esmailpour 的建議。

它生成類似於以下的 SQL:

SELECT `i`.`IceCreamId`, `i`.`Name`
FROM `IceCreams` AS `i`
WHERE LOWER(`i`.`Name`) IN ('cookie', 'berry')
ORDER BY `i`.`IceCreamId`

但是,對於您的情況,這可能還不夠,因為您不是在尋找完全匹配的內容,而是希望返回包含包含關鍵字(以及可能還有其他詞)的字段的行。


AndContainsQuery()使用了第二種方法,它仍然非常簡單,但做了一些稍微不同的事情。 它只返回包含所有關鍵字(可能還有其他詞)的結果。

它生成類似於以下的 SQL:

set @__keyword_0 = 'Cookie';
set @__keyword_1 = 'choco';

SELECT `i`.`IceCreamId`, `i`.`Name`
FROM `IceCreams` AS `i`
WHERE
    (LOCATE(LCASE(@__keyword_0), LCASE(`i`.`Name`)) > 0) AND
    (LOCATE(LCASE(@__keyword_1), LCASE(`i`.`Name`)) > 0)
ORDER BY `i`.`IceCreamId`;

這不是您想要的,但我認為展示也很好,因為它非常簡單,無需手動構建表達式樹即可完成。


最后,第三種方法orContainsQuery() ,並手動構建部分表達式樹。 它構造多個嵌套OR表達式的WHERE表達式的主體。 就是你想要的。

它生成類似於以下的 SQL:

set @__keyword_0 = 'berry';
set @__keyword_1 = 'Cookie';

SELECT `i`.`IceCreamId`, `i`.`Name`
FROM `IceCreams` AS `i`
WHERE
    (LOCATE(LCASE(@__keyword_0), LCASE(`i`.`Name`)) > 0) OR
    (LOCATE(LCASE(@__keyword_1), LCASE(`i`.`Name`)) > 0)
ORDER BY `i`.`IceCreamId`;

這是功能齊全的控制台項目:

using System;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Storage;

namespace IssueConsoleTemplate
{
    public class IceCream
    {
        public int IceCreamId { get; set; }
        public string Name { get; set; }
    }

    public class Context : DbContext
    {
        public DbSet<IceCream> IceCreams { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseMySql(
                    "server=127.0.0.1;port=3306;user=root;password=;database=so60914868",
                    b => b.ServerVersion(new ServerVersion("8.0.20-mysql")))
                .UseLoggerFactory(
                    LoggerFactory.Create(
                        b => b
                            .AddConsole()
                            .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<IceCream>(
                entity =>
                {
                    entity.HasData(
                        new IceCream {IceCreamId = 1, Name = "Vanilla"},
                        new IceCream {IceCreamId = 2, Name = "Berry"},
                        new IceCream {IceCreamId = 3, Name = "Strawberry"},
                        new IceCream {IceCreamId = 4, Name = "Berry & Fruit"},
                        new IceCream {IceCreamId = 5, Name = "cookie"},
                        new IceCream {IceCreamId = 6, Name = "Chocolate chip cookie"},
                        new IceCream {IceCreamId = 7, Name = "Choco-Cookie & Dough"});
                });
        }
    }

    internal class Program
    {
        private static void Main()
        {
            using (var context = new Context())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();
            }

            EqualsQuery();
            AndContainsQuery();
            OrContainsQuery();
        }

        private static void EqualsQuery()
        {
            //
            // This will find only matches that match the word exactly (though case-insensitive):
            //

            using var context = new Context();
            
            var keywords = new[] {"Cookie", "berry"}
                .Select(s => s.ToLower())
                .ToArray();

            var equalsResult = context.IceCreams
                .Where(i => keywords.Contains(i.Name.ToLower()))
                .OrderBy(i => i.IceCreamId)
                .ToList();

            Debug.Assert(equalsResult.Count == 2);
            Debug.Assert(
                equalsResult[0]
                    .Name == "Berry");
            Debug.Assert(
                equalsResult[1]
                    .Name == "cookie");
        }

        private static void AndContainsQuery()
        {
            //
            // This will find matches, that contain ALL keywords (and other words, case-insensitive):
            //

            using var context = new Context();
            
            var keywords = new[] {"Cookie", "choco"};

            var andContainsQuery = context.IceCreams.AsQueryable();

            foreach (var keyword in keywords)
            {
                andContainsQuery = andContainsQuery.Where(i => i.Name.Contains(keyword, StringComparison.CurrentCultureIgnoreCase));
            }

            var andContainsResult = andContainsQuery
                .OrderBy(i => i.IceCreamId)
                .ToList();

            Debug.Assert(andContainsResult.Count == 2);
            Debug.Assert(
                andContainsResult[0]
                    .Name == "Chocolate chip cookie");
            Debug.Assert(
                andContainsResult[1]
                    .Name == "Choco-Cookie & Dough");
        }

        private static void OrContainsQuery()
        {
            //
            // This will find matches, that contains at least one keyword (and other words, case-insensitive):
            //

            using var context = new Context();
            
            var keywords = new[] {"Cookie", "berry"};

            // The lambda parameter.
            var iceCreamParameter = Expression.Parameter(typeof(IceCream), "i");

            // Build the individual conditions to check against.
            var orConditions = keywords
                .Select(keyword => (Expression<Func<IceCream, bool>>) (i => i.Name.Contains(keyword, StringComparison.OrdinalIgnoreCase)))
                .Select(lambda => (Expression) Expression.Invoke(lambda, iceCreamParameter))
                .ToList();

            // Combine the individual conditions to an expression tree of nested ORs.
            var orExpressionTree = orConditions
                .Skip(1)
                .Aggregate(
                    orConditions.First(),
                    (current, expression) => Expression.OrElse(expression, current));
            
            // Build the final predicate (a lambda expression), so we can use it inside of `.Where()`.
            var predicateExpression = (Expression<Func<IceCream, bool>>)Expression.Lambda(
                orExpressionTree,
                iceCreamParameter);

            // Compose and execute the query.
            var orContainsResult = context.IceCreams
                .Where(predicateExpression)
                .OrderBy(i => i.IceCreamId)
                .ToList();

            Debug.Assert(orContainsResult.Count == 6);
            Debug.Assert(orContainsResult[0].Name == "Berry");
            Debug.Assert(orContainsResult[1].Name == "Strawberry");
            Debug.Assert(orContainsResult[2].Name == "Berry & Fruit");
            Debug.Assert(orContainsResult[3].Name == "cookie");
            Debug.Assert(orContainsResult[4].Name == "Chocolate chip cookie");
            Debug.Assert(orContainsResult[5].Name == "Choco-Cookie & Dough");
        }
    }
}

您可以使用Linq中的Any函數來查看數據中是否包含任何術語。

var result = context.Data.Where(data => searchTerms.Any(term => data.Name.Contains(term) ||
                                        searchTerms.Any(term => data.Code.Contains(term));

該請求將在數據庫端執行

var searchTerms = new List<string> { "car", "232" };
Expressions<Func<Data, bool>> expression = it => false;
foreach(var searchTerm in searchTerms)
{
    expression = expression.Or(it => it.Name.Contains(searchTerm));
    //you can implement your own 'Or' extensions method, 
    //or use third-party libraries, i.e. LinqKit
}
var result = context.Data.Where(expression);

您還可以使用規范模式,以實現代碼純度

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM