Giống như Operator trong Entity Framework?


93

Chúng tôi đang cố gắng triển khai toán tử "LIKE" trong Entity Framework cho các thực thể của chúng tôi với các trường chuỗi, nhưng nó dường như không được hỗ trợ. Có ai khác đã cố gắng làm điều gì đó như thế này?

Bài đăng trên blog này tóm tắt vấn đề chúng tôi đang gặp phải. Chúng tôi có thể sử dụng hàm chứa, nhưng điều đó chỉ phù hợp với trường hợp nhỏ nhất cho LIKE. Việc kết hợp chứa, bắt đầu với, kết thúc và indexof đưa chúng ta đến đó, nhưng yêu cầu bản dịch giữa các ký tự đại diện tiêu chuẩn và mã Linq to Entities.


1
Chuyển đến câu trả lời này nếu bạn đang sử dụng EF 6.2.x. Để câu trả lời này nếu bạn đang sử dụng EF Lõi 2.x
CodeNotFound

Câu trả lời:


36

Đây là một bài viết cũ bây giờ, nhưng đối với bất kỳ ai đang tìm kiếm câu trả lời, liên kết này sẽ hữu ích. Chuyển đến câu trả lời này nếu bạn đang sử dụng EF 6.2.x. Đối với câu trả lời này nếu bạn đang sử dụng EF Core 2.x

Phiên bản ngắn:

Phương thức SqlFunctions.PatIndex - trả về vị trí bắt đầu của lần xuất hiện đầu tiên của một mẫu trong một biểu thức được chỉ định hoặc các số không nếu không tìm thấy mẫu, trên tất cả các kiểu dữ liệu ký tự và văn bản hợp lệ

Không gian tên: System.Data.Objects.SqlClient Assembly: System.Data.Entity (trong System.Data.Entity.dll)

Một chút giải thích cũng xuất hiện trong chủ đề diễn đàn này .


59
Câu trả lời được chấp nhận như thế nào là câu trả lời liên kết đến diễn đàn MSDN liên kết ngược lại câu hỏi này với câu trả lời bên dưới ?
Eonasdan 17/10/12

Câu trả lời là sử dụng phương thức SqlFunctions.PatIndex. Chủ đề diễn đàn được liên kết là để cung cấp thêm một chút thông tin "cơ bản".
Yann Duran

Câu trả lời dưới đây là tốt cho các mẫu đơn giản, nhưng nếu tôi muốn nói "WHERE Name LIKE 'abc [0-9]%'" hoặc một số mẫu phức tạp hơn, chỉ cần sử dụng Contains () không hoàn toàn cắt được.
HotN

1
Bản sao của câu trả lời cũ hơn này cho câu hỏi này. (Không phải phần đầu tiên, mà là giải pháp thay thế của nó.)
Frédéric

154

Tôi thực sự không biết gì về EF, nhưng trong LINQ to SQL, bạn thường diễn đạt mệnh đề LIKE bằng cách sử dụng String.

where entity.Name.Contains("xyz")

Dịch sang

WHERE Name LIKE '%xyz%'

(Sử dụng StartsWithEndsWithcho các hành vi khác.)

Tôi không hoàn toàn chắc chắn liệu điều đó có hữu ích hay không, bởi vì tôi không hiểu ý của bạn khi bạn nói rằng bạn đang cố gắng triển khai LIKE. Nếu tôi hiểu sai hoàn toàn, hãy cho tôi biết và tôi sẽ xóa câu trả lời này :)


4
xin vui lòng lưu ý rằng "Ở ĐÂU Tên LIKE '% xyz%'" sẽ không thể sử dụng một chỉ số, vì vậy nếu bảng là rất lớn nó có thể không thực hiện mà cũng ...
Mitch Wheat

1
Chà, chúng tôi muốn có thể kết hợp trên blah * blah foo bar foo? Bar? Foo bar? và các mẫu phức tạp khác. Cách tiếp cận hiện tại của chúng tôi tương tự như những gì bạn đã đề cập, chúng tôi sẽ chuyển đổi các truy vấn đó thành các hoạt động bằng cách sử dụng hàm chứa, chỉ mục, bắt đầu, kết thúc, v.v. Tôi chỉ hy vọng rằng có một giải pháp có mục đích chung hơn.
brien

2
Tôi không biết điều đó - tôi nghi ngờ rằng các mẫu phức tạp sẽ trở nên cụ thể hơn và khó diễn đạt một cách chung chung.
Jon Skeet

4
@Jon Skeet: theo hiểu biết tốt nhất của tôi, chức năng LIKE ở tiêu chuẩn ANSI và nó khá giống trong SQL Server, Oracle và DB2.
AK

2
Một điều tôi đã thấy khi sử dụng các toán tử này và MS SQL là EF thêm chúng dưới dạng tham số thoát "Tên LIKE @ p__linq__1 ESCAPE N '' ~ ''" trong trường hợp sử dụng rất hạn chế của tôi hoạt động chậm hơn rất nhiều nếu chuỗi tìm kiếm chỉ nằm trong truy vấn "Tên như '% xyz%". Đối với các trường hợp tôi gặp phải, tôi vẫn đang sử dụng StartsWith và Contains nhưng tôi thực hiện việc đó thông qua linq động vì điều đó đưa tham số vào câu lệnh SQL mà trong trường hợp của tôi đang tạo ra một . truy vấn hiệu quả hơn không chắc nếu điều này là một điều EF 4.0 hay không Bạn cũng có thể sử dụng ObjectQueryParameters để đạt được điều tương tự ....
Shane Neuville

35

Tôi đã từng gặp vấn đề tương tự.

Hiện tại, tôi đã giải quyết bằng cách lọc Wildcard / Regex phía máy khách dựa trên http://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes?msg=1423024#xx1423024xx - nó đơn giản và hoạt động như hy vọng.

Tôi đã tìm thấy một cuộc thảo luận khác về chủ đề này: http://forums.asp.net/t/1654093.aspx/2/10
Bài đăng này có vẻ hứa hẹn nếu bạn sử dụng Entity Framework> = 4.0:

Sử dụng SqlFunctions.PatIndex:

http://msdn.microsoft.com/en-us/library/system.data.objects.sqlclient.sqlfunctions.patindex.aspx

Như thế này:

var q = EFContext.Products.Where(x =>
SqlFunctions.PatIndex("%CD%BLUE%", x.ProductName) > 0);

Lưu ý: giải pháp này chỉ dành cho SQL-Server vì nó sử dụng hàm PATINDEX không chuẩn.


Trong khi PatIndex "hoạt động", nó sẽ quay lại cắn bạn, PatIndex trong mệnh đề where không sử dụng các chỉ mục trên cột bạn muốn lọc.
BlackICE

@BlackICE điều này được mong đợi. Khi bạn tìm kiếm trên văn bản bên trong (% CD% BLUE%), máy chủ sẽ không thể sử dụng các chỉ mục. Bất cứ khi nào có thể, tìm kiếm văn bản từ đầu (CD% BLUE%) sẽ hiệu quả hơn.
lướt

@surfen patindex tệ hơn thế, nó sẽ không sử dụng chỉ mục ngay cả khi không có% ở phía trước, tìm kiếm (BLUE CD%) với patindex sẽ không sử dụng chỉ mục cột.
BlackICE

23

Cập nhật: Trong EF 6.2 có một toán tử like

Where(obj => DbFunctions.Like(obj.Column , "%expression%")

Đây không phải là một ví dụ rõ ràng hơn Where(obj => DbFunctions.Like(obj.Column , "%expression%"):?
DCD

Chắc chắn rồi. Đã thay đổi nó
Lode Vlaeminck

20

LIKEtoán tử được thêm vào Entity Framework Core 2.0:

var query = from e in _context.Employees
                    where EF.Functions.Like(e.Title, "%developer%")
                    select e;

So sánh với ... where e.Title.Contains("developer") ...nó thực sự được dịch sang SQL LIKEhơn là CHARINDEXchúng ta thấy cho Containsphương pháp.


5

Nó được đề cập cụ thể trong tài liệu như một phần của Entity SQL. Bạn nhận được một thông báo lỗi?

// LIKE and ESCAPE
// If an AdventureWorksEntities.Product contained a Name 
// with the value 'Down_Tube', the following query would find that 
// value.
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name LIKE 'DownA_%' ESCAPE 'A'

// LIKE
Select value P.Name FROM AdventureWorksEntities.Product 
    as P where P.Name like 'BB%'

http://msdn.microsoft.com/en-us/library/bb399359.aspx


1
Tôi muốn tránh xa Entity SQL trong trường hợp bạn muốn rời khỏi EF trong tương lai. Hãy chơi an toàn và thay vào đó hãy sử dụng các tùy chọn Contains (), StartsWith () và EndsWith () trong phản hồi ban đầu.
Stephen Newman

1
Điều đó biên dịch tốt, nhưng không thành công trong thời gian chạy.
brien

Mã tôi đã đăng không thành công trong thời gian chạy? Nó đến từ liên kết Microsoft.
Robert Harvey

Tôi đã chỉnh sửa câu hỏi với một liên kết đến một bài đăng trên blog mô tả cùng một vấn đề mà chúng tôi đang gặp phải.
brien

Có vẻ như Chứa () là vé của bạn. Nhưng như Jon Skeet đã chỉ ra, bạn có thể phải thả xuống một số SQL thực tế đang thao tác trực tiếp cơ sở dữ liệu, nếu Contains không đáp ứng được nhu cầu của bạn.
Robert Harvey

2

nếu bạn đang sử dụng MS Sql, tôi đã viết 2 phương thức mở rộng để hỗ trợ ký tự% cho tìm kiếm theo ký tự đại diện. (LinqKit là bắt buộc)

public static class ExpressionExtension
{
    public static Expression<Func<T, bool>> Like<T>(Expression<Func<T, string>> expr, string likeValue)
    {
        var paramExpr = expr.Parameters.First();
        var memExpr = expr.Body;

        if (likeValue == null || likeValue.Contains('%') != true)
        {
            Expression<Func<string>> valExpr = () => likeValue;
            var eqExpr = Expression.Equal(memExpr, valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(eqExpr, paramExpr);
        }

        if (likeValue.Replace("%", string.Empty).Length == 0)
        {
            return PredicateBuilder.True<T>();
        }

        likeValue = Regex.Replace(likeValue, "%+", "%");

        if (likeValue.Length > 2 && likeValue.Substring(1, likeValue.Length - 2).Contains('%'))
        {
            likeValue = likeValue.Replace("[", "[[]").Replace("_", "[_]");
            Expression<Func<string>> valExpr = () => likeValue;
            var patExpr = Expression.Call(typeof(SqlFunctions).GetMethod("PatIndex",
                new[] { typeof(string), typeof(string) }), valExpr.Body, memExpr);
            var neExpr = Expression.NotEqual(patExpr, Expression.Convert(Expression.Constant(0), typeof(int?)));
            return Expression.Lambda<Func<T, bool>>(neExpr, paramExpr);
        }

        if (likeValue.StartsWith("%"))
        {
            if (likeValue.EndsWith("%") == true)
            {
                likeValue = likeValue.Substring(1, likeValue.Length - 2);
                Expression<Func<string>> valExpr = () => likeValue;
                var containsExpr = Expression.Call(memExpr, typeof(String).GetMethod("Contains",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(containsExpr, paramExpr);
            }
            else
            {
                likeValue = likeValue.Substring(1);
                Expression<Func<string>> valExpr = () => likeValue;
                var endsExpr = Expression.Call(memExpr, typeof(String).GetMethod("EndsWith",
                    new[] { typeof(string) }), valExpr.Body);
                return Expression.Lambda<Func<T, bool>>(endsExpr, paramExpr);
            }
        }
        else
        {
            likeValue = likeValue.Remove(likeValue.Length - 1);
            Expression<Func<string>> valExpr = () => likeValue;
            var startsExpr = Expression.Call(memExpr, typeof(String).GetMethod("StartsWith",
                new[] { typeof(string) }), valExpr.Body);
            return Expression.Lambda<Func<T, bool>>(startsExpr, paramExpr);
        }
    }

    public static Expression<Func<T, bool>> AndLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var andPredicate = Like(expr, likeValue);
        if (andPredicate != null)
        {
            predicate = predicate.And(andPredicate.Expand());
        }
        return predicate;
    }

    public static Expression<Func<T, bool>> OrLike<T>(this Expression<Func<T, bool>> predicate, Expression<Func<T, string>> expr, string likeValue)
    {
        var orPredicate = Like(expr, likeValue);
        if (orPredicate != null)
        {
            predicate = predicate.Or(orPredicate.Expand());
        }
        return predicate;
    }
}

sử dụng

var orPredicate = PredicateBuilder.False<People>();
orPredicate = orPredicate.OrLike(per => per.Name, "He%llo%");
orPredicate = orPredicate.OrLike(per => per.Name, "%Hi%");

var predicate = PredicateBuilder.True<People>();
predicate = predicate.And(orPredicate.Expand());
predicate = predicate.AndLike(per => per.Status, "%Active");

var list = dbContext.Set<People>().Where(predicate.Expand()).ToList();    

trong ef6 và nó sẽ dịch sang

....
from People per
where (
    patindex(@p__linq__0, per.Name) <> 0
    or per.Name like @p__linq__1 escape '~'
) and per.Status like @p__linq__2 escape '~'

', @ p__linq__0 ='% He% llo% ', @ p__linq__1 ='% Hi% ', @ p__linq_2 ='% Active '


cảm ơn bình luận của bạn Ronel, tôi có thể giúp gì được không? thông báo lỗi là gì?
Steven Chong

2

Đối với EfCore, đây là một mẫu để xây dựng biểu thức LIKE

protected override Expression<Func<YourEntiry, bool>> BuildLikeExpression(string searchText)
    {
        var likeSearch = $"%{searchText}%";

        return t => EF.Functions.Like(t.Code, likeSearch)
                    || EF.Functions.Like(t.FirstName, likeSearch)
                    || EF.Functions.Like(t.LastName, likeSearch);
    }

//Calling method

var query = dbContext.Set<YourEntity>().Where(BuildLikeExpression("Text"));

0

Bạn có thể sử dụng một lượt thích thực trong Liên kết với các thực thể khá dễ dàng

Thêm vào

    <Function Name="String_Like" ReturnType="Edm.Boolean">
      <Parameter Name="searchingIn" Type="Edm.String" />
      <Parameter Name="lookingFor" Type="Edm.String" />
      <DefiningExpression>
        searchingIn LIKE lookingFor
      </DefiningExpression>
    </Function>

cho EDMX của bạn trong thẻ này:

edmx: Edmx / edmx: Runtime / edmx: ConceptualModels / Schema

Cũng nhớ không gian tên trong <schema namespace="" />thuộc tính

Sau đó, thêm một lớp mở rộng trong không gian tên trên:

public static class Extensions
{
    [EdmFunction("DocTrails3.Net.Database.Models", "String_Like")]
    public static Boolean Like(this String searchingIn, String lookingFor)
    {
        throw new Exception("Not implemented");
    }
}

Phương thức mở rộng này bây giờ sẽ ánh xạ tới hàm EDMX.

Thông tin thêm tại đây: http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.