LINQ Chứa trường hợp không nhạy cảm


174

Mã này là trường hợp nhạy cảm, làm thế nào để làm cho nó không nhạy cảm?

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM.Where(fi => fi.DESCRIPTION.Contains(description));
}

Câu trả lời của Sjoerd là chính xác nhưng ... Tôi muốn nhận kết quả tìm kiếm cho tên bằng tiếng Thổ Nhĩ Kỳ (ví dụ) khi viết i và ngược lại. Trong trường hợp này, ToLower dường như là cách chính xác để đi. Vui long sửa cho tôi nêu tôi sai. Giới thiệu về tiếng Thổ Nhĩ Kỳ: en.wikipedia.org/wiki/Diated_and_dotless_I
Anh Nrik

@HeNrik - Như đã thảo luận ở Thổ Nhĩ Kỳ Liên kết kiểm tra trong nhận xét của JYelton theo câu trả lời được chấp nhận, khi chạy với văn hóa Thổ Nhĩ Kỳ, hai chữ cái đó của tôi sẽ khác - vì vậy bạn sẽ không tìm thấy tên với i khác. Bạn muốn ToLowerInvariant. Xem thảo luận dưới nhiều câu trả lời ở đây .
ToolmakerSteve

đây là một câu hỏi cũ, nhưng đáng lưu ý rằng trong phiên bản hiện tại, EF core 2.0 ToLower () hoạt động như người sau. Where (p => p.Name.ToLower (). Chứa (myParam.Name.ToLower () )); Tôi đang sử dụng điều này trong một truy vấn Linq đối với DB Postgres. Tôi không có trường hợp không nhạy cảm với đối chiếu cột trong DB và tôi đã kiểm tra rằng nếu không có ToLower () thì trận đấu rõ ràng là trường hợp nhạy cảm.
Shelbypereira

Câu trả lời:


72

Giả sử chúng ta đang làm việc với các chuỗi ở đây, đây là một giải pháp "thanh lịch" khác bằng cách sử dụng IndexOf().

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
        .Where(fi => fi.DESCRIPTION
                       .IndexOf(description, StringComparison.OrdinalIgnoreCase) != -1);
}

7
Đẹp. Đối với mục đích riêng của tôi, điều này không hoạt động cho LINQ to Entities. Giải pháp tốt đẹp cho LINQ cho các đối tượng mặc dù.
Damian Powell

242
fi => fi.DESCRIPTION.ToLower().Contains(description.ToLower())

49
Như Jon Skeet đã nhận xét về một câu hỏi liên quan , phương pháp này sẽ không vượt qua Bài kiểm tra Thổ Nhĩ Kỳ .
JYelton

5
Không, nhưng cơ sở dữ liệu hoạt động với các bộ ký tự và đối chiếu. Nếu bạn đang cố gắng đẩy công việc vào cơ sở dữ liệu, bạn phải đưa ra một số giả định về bộ ký tự và đối chiếu, phải không?
Christopher Stevenson

66
Chứa nên sử dụng IEqualityComparer<string>thuộc tính để xử lý việc so sánh sẽ hoạt động như thế nào. Sử dụng ToLower và ToUpper để kiểm tra sự bình đẳng là một ý tưởng tồi. Hãy thử: .Contains(description, StringComparer.CurrentCultureIgnoreCase)ví dụ
Dorival

19
Nhận xét từ @Dorival không hoạt động, vì nó đưa ra lỗi này:Error 1 'string' does not contain a definition for 'Contains' and the best extension method overload 'System.Linq.ParallelEnumerable.Contains<TSource>(System.Linq.ParallelQuery<TSource>, TSource, System.Collections.Generic.IEqualityComparer<TSource>)' has some invalid arguments
eMi

6
Containsvới StringComparerkhông nhận được chuỗi như tham số, vì vậy nó sẽ được xây dựng-lỗi. IndexOftrên Queryablecó lẽ không thể được dịch sang SQL. Cá nhân tôi thấy câu trả lời này hoàn toàn hợp lệ khi chúng ta nói về LINQ với cơ sở dữ liệu.
Thariq Nugrohotomo

122

Nếu truy vấn LINQ được thực thi trong ngữ cảnh cơ sở dữ liệu, một lệnh gọi Contains()được ánh xạ tới LIKEtoán tử:

.Where(a => a.Field.Contains("hello")) trở thành Field LIKE '%hello%'. Các LIKEnhà điều hành là trường hợp nhạy cảm theo mặc định, nhưng có thể được thay đổi bằng cách thay đổi collation của cột .

Nếu truy vấn LINQ được thực thi trong ngữ cảnh .NET, bạn có thể sử dụng IndexOf () , nhưng phương thức đó không được hỗ trợ trong LINQ to SQL.

LINQ to SQL không hỗ trợ các phương thức lấy tham số CultureInfo làm tham số, có lẽ vì nó không thể đảm bảo rằng máy chủ SQL xử lý các nền văn hóa giống như .NET. Đây không phải là hoàn toàn đúng, bởi vì nó không hỗ trợ StartsWith(string, StringComparison).

Tuy nhiên, nó dường như không hỗ trợ một phương thức đánh giá LIKEtrong LINQ to SQL và so sánh trường hợp không nhạy cảm trong .NET, khiến cho không thể thực hiện các trường hợp không phân biệt chữ hoa chữ thường () theo cách nhất quán.


Chỉ FYI EF 4.3 không hỗ trợ StartsWith. Tôi nhận được: LINQ to Entities không nhận ra phương thức 'Boolean StartsWith (System.String, System.StringComparison)'
nakhli 19/12/13

StartWith chuyển đổi thành THÍCH 'xin chào%'?
Bart Calixto

liên kết clicdata đã chết.
Adam Parkin

2
nỗ lực rất lớn để đào sâu vào hành vi SQL và db được tạo cho mệnh đề THÍCH
Thariq Nugrohotomo

1
Vì vậy, các tùy chọn là gì khi sử dụng EF, Trong một ngữ cảnh tôi cần thực hiện insensitivetìm kiếm trường hợp và trong trường hợp khác tôi cần nó case sensitive. Tôi có phải gõ tiếng gõ hiệu suất và sử dụng 'toLower ()' không?
Zapnologica

12

Câu trả lời được chấp nhận ở đây không đề cập đến một thực tế là nếu bạn có chuỗi null ToLower () sẽ đưa ra một ngoại lệ. Cách an toàn hơn sẽ là:

fi => (fi.DESCRIPTION ?? string.Empty).ToLower().Contains((description ?? string.Empty).ToLower())

Bạn không thể tạo một ngoại lệ trên một truy vấn được dịch sang SQL
Alex Zhukovskiy

@AlexZhukovskiy Làm thế nào mà thậm chí có liên quan đến vấn đề này? Nếu fi.DESCRIPTION là null hoặc mô tả là null, bạn sẽ nhận được ngoại lệ tham chiếu C # null. Không có vấn đề gì khi truy vấn LINQ chuyển đổi sang phía SQL. Đây là bằng chứng: dotnetfiddle.net/5pZ1dY
Marko

Bởi vì truy vấn này sẽ không dịch sang SQL vì nó không hỗ trợ toán tử kết nối null. Và bạn có thể truy vấn cơ sở dữ liệu thay vì tải ra tất cả các mục để sử dụng null thaneshing ở phía máy khách. Vì vậy, nếu bạn sử dụng nó - nó ổn ở phía máy khách nhưng không thành công với DB, nếu không thì bạn vẫn ổn với DB và bạn không quan tâm đến nullref ở phía máy khách vì điều đó sẽ không xảy ra vì C # không thực hiện truy vấn này và không 'T thực sự đọc các đối tượng null.
Alex Zhukovskiy

Câu trả lời này đã giúp tôi giải quyết một vấn đề mà tôi đang gặp phải với LINQ cho các Thực thể nơi tôi đang làm. IndexOf và. Tiếp tục trên một IEnumerable trong đó giá trị chuỗi đến từ cơ sở dữ liệu là null. Tôi đã không nhận được lỗi cho đến khi kết quả được liệt kê và sau đó tôi nhận được thông báo lỗi rằng "Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng." Tôi không thể hiểu tại sao nó lại xảy ra cho đến khi tôi thấy bài đăng này. Cảm ơn!
randyh22

7

Sử dụng C # 6.0 (cho phép các hàm thân biểu thức và lan truyền null), đối với LINQ đến các Đối tượng, nó có thể được thực hiện trong một dòng như thế này (cũng kiểm tra null):

public static bool ContainsInsensitive(this string str, string value) => str?.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0;

Nó không hoạt động vì ContainsInsensitive không phải là lệnh của cửa hàng
Sven

@Sven - vâng, nó chỉ hoạt động cho LINQ to Object. Tôi đã sửa câu trả lời của tôi. Cảm ơn.
Alexei

4

IndexOf hoạt động tốt nhất trong trường hợp này

return this
   .ObjectContext
   .FACILITY_ITEM
   .Where(fi => fi.DESCRIPTION.IndexOf(description, StringComparison.OrdinalIgnoreCase)>=0);

3

Bạn có thể sử dụng chuỗi.

    lst.Where(x => string.Compare(x,"valueToCompare",StringComparison.InvariantCultureIgnoreCase)==0);

nếu bạn chỉ muốn kiểm tra có chứa thì hãy sử dụng "Bất kỳ"

  lst.Any(x => string.Compare(x,"valueToCompare",StringComparison.InvariantCultureIgnoreCase)==0)

Điều này không trả lời câu hỏi. OP đang hỏi về 'Chứa' trong một chuỗi (nghĩa là một chuỗi chứa chuỗi khác), không phải là một tập hợp các chuỗi có chứa một chuỗi hay không.
andrewf

1
public static bool Contains(this string input, string findMe, StringComparison comparisonType)
{
    return String.IsNullOrWhiteSpace(input) ? false : input.IndexOf(findMe, comparisonType) > -1;
}

2
chúng ta có thể sử dụng các phương thức mở rộng tùy chỉnh trong các truy vấn linq không? bạn có chắc không ?
Vishal Sharma


0

Thành thật mà nói, điều này không cần phải khó khăn. Có vẻ như trên khởi phát, nhưng nó không phải là. Đây là một truy vấn linq đơn giản trong C # thực hiện chính xác như yêu cầu.

Trong ví dụ của tôi, tôi đang làm việc với một danh sách những người có một tài sản được gọi là FirstName.

var results = ClientsRepository().Where(c => c.FirstName.ToLower().Contains(searchText.ToLower())).ToList();

Điều này sẽ tìm kiếm cơ sở dữ liệu về tìm kiếm chữ thường nhưng trả về kết quả trường hợp đầy đủ.


-2

Sử dụng phương thức String.Equals

public IQueryable<FACILITY_ITEM> GetFacilityItemRootByDescription(string description)
{
    return this.ObjectContext.FACILITY_ITEM
           .Where(fi => fi.DESCRIPTION
           .Equals(description, StringComparison.OrdinalIgnoreCase));
}
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.