So sánh chuỗi không phân biệt chữ hoa chữ thường trong LINQ-to-SQL


137

Tôi đã đọc rằng việc sử dụng ToUpper và ToLower để thực hiện so sánh chuỗi không phân biệt chữ hoa chữ thường, nhưng tôi không thấy sự thay thế nào khi nói đến LINQ-to-SQL. Các đối số ignCase và soOptions của String.Compare bị LINQ-to-SQL bỏ qua (nếu bạn đang sử dụng cơ sở dữ liệu phân biệt chữ hoa chữ thường, bạn sẽ có được một so sánh phân biệt chữ hoa chữ thường ngay cả khi bạn yêu cầu so sánh không phân biệt chữ hoa chữ thường). ToLower hay ToUpper là lựa chọn tốt nhất ở đây? Cái này tốt hơn những cái khác phải không? Tôi nghĩ rằng tôi đã đọc ở đâu đó rằng ToUpper tốt hơn, nhưng tôi không biết nếu điều đó áp dụng ở đây. (Tôi đang thực hiện nhiều đánh giá mã và mọi người đang sử dụng ToLower.)

Dim s = From row In context.Table Where String.Compare(row.Name, "test", StringComparison.InvariantCultureIgnoreCase) = 0

Điều này chuyển thành một truy vấn SQL chỉ đơn giản là so sánh hàng. Tham khảo với "test" và sẽ không trả về "Test" và "TEST" trên cơ sở dữ liệu phân biệt chữ hoa chữ thường.


1
Cảm ơn! Điều này thực sự đã cứu ass của tôi ngày hôm nay. Lưu ý: nó hoạt động với các phần mở rộng LINQ khác quá thích LINQQuery.Contains("VaLuE", StringComparer.CurrentCultureIgnoreCase)LINQQuery.Except(new string[]{"A VaLUE","AnOTher VaLUE"}, StringComparer.CurrentCultureIgnoreCase). Wahoo!
Greg Bray

Thật buồn cười, tôi vừa đọc rằng ToUpper tốt hơn khi so sánh từ nguồn này: msdn.microsoft.com/en-us/l
Library / dd465121

Câu trả lời:


110

Như bạn nói, có một số khác biệt quan trọng giữa ToUpper và ToLower, và chỉ có một sự chính xác đáng tin cậy khi bạn đang cố gắng thực hiện kiểm tra bình đẳng không nhạy cảm.

Lý tưởng nhất, cách tốt nhất để thực hiện kiểm tra bình đẳng không phân biệt chữ hoa chữ thường :

String.Equals(row.Name, "test", StringComparison.OrdinalIgnoreCase)

LƯU Ý, TUY NHIÊN rằng điều này không hoạt động trong trường hợp này! Vì vậy, chúng tôi đang bị mắc kẹt với ToUpperhoặc ToLower.

Lưu ý thứ tự IgnoreCase để làm cho nó an ninh-an toàn. Nhưng chính xác loại trường hợp (trong) kiểm tra nhạy cảm bạn sử dụng phụ thuộc vào mục đích của bạn là gì. Nhưng nói chung, hãy sử dụng Equals để kiểm tra đẳng thức và So sánh khi bạn sắp xếp, sau đó chọn StringComparison phù hợp cho công việc.

Michael Kaplan (một cơ quan được công nhận về văn hóa và xử lý nhân vật như thế này) có các bài đăng liên quan trên ToUpper so với ToLower:

Anh ta nói "String.ToUpper - Sử dụng ToUpper chứ không phải ToLower và chỉ định InvariantCARM để chọn quy tắc vỏ của hệ điều hành "


1
Có vẻ như điều này không áp dụng cho SQL Server: in phía trên ('Große Straße') trả về GROßE STRAßE
BlueMonkMN

1
Ngoài ra, mã mẫu bạn cung cấp có cùng vấn đề với mã tôi đã cung cấp theo phân biệt chữ hoa chữ thường khi chạy qua LINQ-to-SQL trên cơ sở dữ liệu MS SQL 2005.
BlueMonkMN

2
Tôi đồng ý. Xin lỗi tôi đã không rõ ràng. Mã mẫu tôi cung cấp không hoạt động với Linq2Sql như bạn đã chỉ ra trong câu hỏi ban đầu của mình. Tôi chỉ đơn thuần nghỉ ngơi rằng cách bạn bắt đầu là một cách tuyệt vời để đi - nếu nó chỉ hoạt động trong kịch bản này. Và vâng, một hộp xà phòng khác của Mike Kaplan là việc xử lý nhân vật của SQL Server ở khắp mọi nơi. Nếu bạn cần phân biệt chữ hoa chữ thường và không thể hiểu theo bất kỳ cách nào khác, tôi đã gợi ý (không rõ ràng) rằng bạn lưu trữ dữ liệu dưới dạng chữ hoa, và sau đó truy vấn nó dưới dạng chữ hoa.
Andrew Arnott

3
Chà, nếu bạn có một cơ sở dữ liệu phân biệt chữ hoa chữ thường và bạn lưu trữ trong trường hợp hỗn hợp và tìm kiếm trong chữ hoa, bạn sẽ không nhận được kết quả khớp. Nếu bạn viết hoa cả dữ liệu và truy vấn trong tìm kiếm của mình, thì bạn đang chuyển đổi tất cả văn bản bạn đang tìm kiếm cho mọi truy vấn không phù hợp.
Andrew Arnott

1
@BlueMonkMN, bạn có chắc bạn đã dán đúng đoạn trích không? Thật khó tin Máy chủ MSSQL thích Màu đỏ hơn Màu đen.
greenoldman

75

Tôi đã sử dụng System.Data.Linq.SqlClient.SqlMethods.Like(row.Name, "test") trong truy vấn của tôi.

Điều này thực hiện một so sánh không nhạy cảm trường hợp.


3
ha! đã sử dụng linq 2 sql trong vài năm nay nhưng không thấy SqlMethods cho đến bây giờ, cảm ơn!
Carl Hörberg

3
Xuất sắc! Có thể sử dụng chi tiết hơn, mặc dù. Đây có phải là một trong những ứng dụng được mong đợi của Like không? Có đầu vào nào có thể gây ra kết quả dương tính giả không? Hoặc một kết quả âm tính giả? Tài liệu về phương pháp này còn thiếu, tài liệu nào sẽ mô tả hoạt động của phương thức Like?
Nhiệm vụ

2
Tôi nghĩ rằng nó chỉ dựa vào cách SQL Server so sánh các chuỗi, có thể cấu hình ở đâu đó.
Andrew Davey

11
System.Data.Linq.SqlClient.SqlMethods.Like (row.Name, "test") giống như row.Name.Contains ("test"). Như Andrew đang nói, điều này phụ thuộc vào sự đối chiếu của máy chủ sql. Vì vậy, Like (hoặc chứa) không phải lúc nào cũng thực hiện so sánh không phân biệt chữ hoa chữ thường.
doekman

3
Hãy lưu ý, điều này làm cho mã quá đôi SqlClient.
Jaider

5

Tôi đã thử điều này bằng cách sử dụng biểu thức Lambda, và nó đã hoạt động.

List<MyList>.Any (x => (String.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)) && (x.Type == qbType) );


18
Đó là bởi vì bạn đang sử dụng một List<>, có nghĩa là việc so sánh diễn ra trong bộ nhớ (mã C #) chứ không phải là IQueryable(hoặc ObjectQuery) sẽ thực hiện so sánh trong cơ sở dữ liệu .
drzaus

1
Những gì @drzaus nói. Câu trả lời này đơn giản là sai, xem xét rằng bối cảnh là linq2sql, và không phải linq thông thường.
rsenna

0

Nếu bạn truyền một chuỗi không phân biệt chữ hoa chữ thường vào LINQ-to-SQL, nó sẽ được chuyển vào SQL không thay đổi và việc so sánh sẽ xảy ra trong cơ sở dữ liệu. Nếu bạn muốn thực hiện so sánh chuỗi không phân biệt chữ hoa chữ thường trong cơ sở dữ liệu, tất cả những gì bạn cần làm là tạo một biểu thức lambda để so sánh và nhà cung cấp LINQ-to-SQL sẽ dịch nguyên biểu thức đó thành truy vấn SQL với chuỗi của bạn.

Ví dụ: truy vấn LINQ này:

from user in Users
where user.Email == "foo@bar.com"
select user

được nhà cung cấp LINQ-to-SQL dịch sang SQL sau đây:

SELECT [t0].[Email]
FROM [User] AS [t0]
WHERE [t0].[Email] = @p0
-- note that "@p0" is defined as nvarchar(11)
-- and is passed my value of "foo@bar.com"

Như bạn có thể thấy, tham số chuỗi sẽ được so sánh trong SQL, điều đó có nghĩa là mọi thứ phải hoạt động theo cách bạn mong đợi.


Tôi không hiểu bạn đang nói gì. 1) Bản thân các chuỗi không thể phân biệt chữ hoa chữ thường hoặc phân biệt chữ hoa chữ thường trong .NET, vì vậy tôi không thể vượt qua "chuỗi không phân biệt chữ hoa chữ thường". 2) Một truy vấn LINQ về cơ bản là biểu thức lambda và đó là cách tôi chuyển hai chuỗi của mình, vì vậy điều này không có ý nghĩa gì với tôi.
BlueMonkMN

3
Tôi muốn thực hiện so sánh CASE-INSENSITIVE trên cơ sở dữ liệu CASE-SENSITIVE.
BlueMonkMN

Bạn đang sử dụng cơ sở dữ liệu CASE-SENSITIVE nào?
Andrew Hare

Ngoài ra, một truy vấn LINQ không phải là biểu thức lambda. Một truy vấn LINQ bao gồm một số phần (đáng chú ý nhất là các toán tử truy vấn và biểu thức lambda).
Andrew Hare

Câu trả lời này không có ý nghĩa như các bình luận của BlueMonkMN.
Alf

0

Để thực hiện các truy vấn Linq tới Sql phân biệt chữ hoa chữ thường khai báo các trường 'chuỗi' phân biệt chữ hoa chữ thường bằng cách chỉ định kiểu dữ liệu máy chủ bằng cách sử dụng một trong các cách sau;

varchar(4000) COLLATE SQL_Latin1_General_CP1_CS_AS 

hoặc là

nvarchar(Max) COLLATE SQL_Latin1_General_CP1_CS_AS

Lưu ý: 'CS' trong các loại đối chiếu ở trên có nghĩa là 'Phân biệt chữ hoa chữ thường'.

Điều này có thể được nhập vào trường Loại dữ liệu của Máy chủ trong khi xem một thuộc tính bằng Visual Studio DBML Designer.

Để biết thêm chi tiết, hãy xem http://yourdotnetdesignteam.blogspot.com/2010/06/case-sensitive-linq-to-sql-queries.html


Đó là vấn đề. Thông thường lĩnh vực tôi sử dụng là trường hợp nhạy cảm (công thức hóa học CO [carbon monoxide] khác với Co [coban]). Tuy nhiên, trong một tình huống cụ thể (tìm kiếm) tôi muốn co khớp với cả Co và CO. Xác định một thuộc tính bổ sung với "kiểu dữ liệu máy chủ" khác là không hợp pháp (linq to sql chỉ cho phép một thuộc tính trên mỗi cột sql). Thế là vẫn không đi.
doekman

Ngoài ra, nếu thực hiện Kiểm thử đơn vị, phương pháp này sẽ không có khả năng tương thích với giả dữ liệu. Tốt nhất để sử dụng phương pháp linq / lambda trong câu trả lời được chấp nhận.
Derrick

0
where row.name.StartsWith(q, true, System.Globalization.CultureInfo.CurrentCulture)

1
Văn bản SQL được dịch là gì và điều gì cho phép nó không phân biệt chữ hoa chữ thường trong môi trường SQL có thể coi nó là phân biệt chữ hoa chữ thường?
BlueMonkMN

0

Cách tiếp cận 2 giai đoạn sau đây phù hợp với tôi (VS2010, ASP.NET MVC3, SQL Server 2008, Linq to SQL):

result = entRepos.FindAllEntities()
    .Where(e => e.EntitySearchText.Contains(item));

if (caseSensitive)
{
    result = result
        .Where(e => e.EntitySearchText.IndexOf(item, System.StringComparison.CurrentCulture) >= 0);
}

1
Mã này có lỗi nếu văn bản bắt đầu bằng văn bản tìm kiếm (nên> = 0)
Flatliner DOA

@FlatlinerDOA thực sự phải là != -1IndexOf "trả về -1 nếu không tìm thấy ký tự hoặc chuỗi"
drzaus

0

Đôi khi giá trị được lưu trữ trong Cơ sở dữ liệu có thể chứa khoảng trắng nên việc chạy này có thể bị lỗi

String.Equals(row.Name, "test", StringComparison.OrdinalIgnoreCase)

Giải pháp cho vấn đề này là loại bỏ không gian sau đó chuyển đổi trường hợp của nó sau đó chọn như thế này

 return db.UsersTBs.Where(x => x.title.ToString().ToLower().Replace(" ",string.Empty).Equals(customname.ToLower())).FirstOrDefault();

Lưu ý trong trường hợp này

customname là giá trị để khớp với giá trị Cơ sở dữ liệu

Người dùngTB là lớp

tiêu đề là cột Cơ sở dữ liệu


-1

Hãy nhớ rằng có một sự khác biệt giữa việc truy vấn có hoạt động hay không và nó có hoạt động hiệu quả không ! Một câu lệnh LINQ được chuyển đổi thành T-SQL khi mục tiêu của câu lệnh là SQL Server, vì vậy bạn cần suy nghĩ về T-SQL sẽ được tạo ra.

Sử dụng String.Equals rất có thể (tôi đoán) sẽ mang lại tất cả các hàng từ SQL Server và sau đó thực hiện so sánh trong .NET, vì đó là biểu thức .NET không thể dịch sang T-SQL.

Nói cách khác, sử dụng một biểu thức sẽ tăng quyền truy cập dữ liệu của bạn và loại bỏ khả năng sử dụng các chỉ mục. Nó sẽ hoạt động trên các bàn nhỏ và bạn sẽ không nhận thấy sự khác biệt. Trên một cái bàn lớn, nó có thể hoạt động rất tệ.

Đó là một trong những vấn đề tồn tại với LINQ; mọi người không còn nghĩ về những tuyên bố họ viết sẽ được thực hiện như thế nào.

Trong trường hợp này, không có cách nào để làm những gì bạn muốn mà không sử dụng biểu thức - ngay cả trong T-SQL. Do đó, bạn có thể không thể làm điều này hiệu quả hơn. Ngay cả câu trả lời T-SQL được đưa ra ở trên (sử dụng các biến có đối chiếu) rất có thể sẽ khiến các chỉ mục bị bỏ qua, nhưng nếu đó là một bảng lớn thì đáng để chạy câu lệnh và xem xét kế hoạch thực hiện để xem liệu chỉ mục có được sử dụng không .


2
Điều đó không đúng (nó không khiến các hàng được trả về máy khách). Tôi đã sử dụng String.Equals và lý do nó không hoạt động là vì nó được chuyển đổi thành so sánh chuỗi TSQL, hành vi của chúng phụ thuộc vào sự đối chiếu của cơ sở dữ liệu hoặc máy chủ. Tôi xin xem xét mọi biểu thức LINQ to SQL tôi viết sẽ được chuyển đổi thành TSQL như thế nào. Cách để tôi muốn là sử dụng ToUpper để buộc TSQL được tạo phải sử dụng UPPER. Sau đó, tất cả logic chuyển đổi và so sánh vẫn được thực hiện trong TSQL để bạn không mất nhiều hiệu suất.
BlueMonkMN
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.