So sánh phân biệt chữ hoa chữ thường LINQ với Đối tượng


115

Đây không phải là so sánh phân biệt chữ hoa chữ thường trong LINQ với các Đối tượng:

Thingies.First(t => t.Name == "ThingamaBob");

Làm cách nào để có thể so sánh phân biệt chữ hoa chữ thường với LINQ với các Đối tượng?


@Ronnie: bạn có chắc về điều đó không? Ý bạn là so sánh không phân biệt chữ hoa chữ thường ?
Michael Petrotta

14
Hoàn toàn chắc chắn. Không, tôi không có ý đó.
Ronnie Overby

12
Không, trên máy tính của tôi đang chạy EF 4.0 w / SQL Server 2008 R2, phần trên không phân biệt chữ hoa chữ thường. Tôi biết nhiều nơi nói rằng EF phân biệt chữ hoa chữ thường mặc định, nhưng đó không phải là những gì tôi đã trải qua.
tster

3
Điều đó sẽ không phụ thuộc vào cơ sở dữ liệu bên dưới chứ?
codymanix

1
@codymanix: Đó là một câu hỏi hay! Linq sang EF có dịch biểu thức lambda cho một truy vấn DB không? Tôi không biết câu trả lời.
Tergiver

Câu trả lời:


163

Đó là bởi vì bạn đang sử dụng LINQ To Entities , cuối cùng chuyển đổi các biểu thức Lambda của bạn thành các câu lệnh SQL. Điều đó có nghĩa là phân biệt chữ hoa chữ thường tùy thuộc vào Máy chủ SQL của bạn mà theo mặc định có SQL_Latin1_General_CP1_CI_AS Collation và điều đó KHÔNG phân biệt chữ hoa chữ thường.

Sử dụng ObjectQuery.ToTraceString để xem truy vấn SQL được tạo đã thực sự được gửi đến SQL Server tiết lộ bí ẩn:

string sqlQuery = ((ObjectQuery)context.Thingies
        .Where(t => t.Name == "ThingamaBob")).ToTraceString();

Khi bạn tạo truy vấn LINQ to Entities , LINQ to Entities sẽ sử dụng trình phân tích cú pháp LINQ để bắt đầu xử lý truy vấn và chuyển nó thành cây biểu thức LINQ. Sau đó, cây biểu thức LINQ được chuyển đến API dịch vụ đối tượng , nó chuyển đổi cây biểu thức thành cây lệnh. Sau đó, nó được gửi đến nhà cung cấp cửa hàng (ví dụ: SqlClient), nơi chuyển cây lệnh thành văn bản lệnh cơ sở dữ liệu gốc. Truy vấn được thực thi trên kho dữ liệu và kết quả được Vật chất hóa thành các Đối tượng Thực thể bởi Dịch vụ Đối tượng. Không có logic nào được đưa vào giữa để tính đến phân biệt chữ hoa chữ thường. Vì vậy, bất kể trường hợp nào bạn đặt trong vị ngữ của mình, nó sẽ luôn được SQL Server của bạn coi như vậy trừ khi bạn thay đổi SQL Server Collates cho cột đó.

Giải pháp phía máy chủ:

Do đó, giải pháp tốt nhất sẽ là thay đổi đối chiếu của cột Tên trong bảng Thingies thành COLLATE Latin1_General_CS_AS phân biệt chữ hoa chữ thường bằng cách chạy điều này trên Máy chủ SQL của bạn:

ALTER TABLE Thingies
ALTER COLUMN Name VARCHAR(25)
COLLATE Latin1_General_CS_AS

Để biết thêm thông tin về SQL Server Collates , hãy xem phần Tìm kiếm truy vấn SQL phân biệt chữ hoa chữ thường trên SQL SERVER Collate

Giải pháp phía khách hàng:

Giải pháp duy nhất mà bạn có thể áp dụng ở phía máy khách là sử dụng LINQ to Objects để thực hiện một phép so sánh khác có vẻ không được thanh lịch cho lắm:

Thingies.Where(t => t.Name == "ThingamaBob")
        .AsEnumerable()
        .First(t => t.Name == "ThingamaBob");

Tôi đang tạo lược đồ cơ sở dữ liệu với Entity Framework, vì vậy giải pháp sử dụng mã gọi của tôi sẽ là tốt nhất. Tôi đoán tôi sẽ kiểm tra sau khi có kết quả. Cảm ơn.
Ronnie Overby

Không vấn đề gì. Vâng, điều đó chính xác và tôi đã cập nhật câu trả lời của mình bằng giải pháp phía khách hàng, tuy nhiên nó không được thanh lịch lắm và tôi vẫn khuyên bạn nên sử dụng giải pháp lưu trữ dữ liệu.
Morteza Manavi

18
@eglasius Điều này không hoàn toàn đúng: Nó không tìm nạp TẤT CẢ dữ liệu, nó chỉ tìm nạp dữ liệu khớp với chữ hoa và chữ thường, và sau đó nó được lọc lại trên trường hợp khách một cách nhạy cảm. Tất nhiên, nếu bạn tình cờ có hàng nghìn mục nhập không phân biệt chữ hoa chữ thường, nhưng chỉ một trong số chúng là đúng một phân biệt chữ hoa chữ thường, thì đó là rất nhiều chi phí. Nhưng tôi không nghĩ rằng thực tế sẽ đưa ra những kịch bản như vậy ... :)
Achim

1
@MassoodKhaari Giải pháp bạn đã đăng sẽ làm cho nó Không phân biệt chữ hoa chữ thường vì bạn viết hoa thấp hơn cả hai mặt của so sánh. OP cần so sánh phân biệt chữ hoa chữ thường.
Jonny

1
"Do đó, giải pháp tốt nhất sẽ là thay đổi đối chiếu của cột Tên trong bảng Thingies thành COLLATE Latin1_General_CS_AS" - Tôi không nghĩ rằng đây là cách tốt nhất. Hầu hết thời gian tôi cần bộ lọc LIKE không phân biệt chữ hoa chữ thường (.Contains ()) nhưng đôi khi nó phải phân biệt chữ hoa chữ thường. Tôi sẽ thử "Giải pháp phía máy khách" của bạn - nó thanh lịch hơn nhiều cho trường hợp sử dụng của tôi, tôi nghĩ (sẽ rất tuyệt nếu hiểu nó làm gì nhưng bạn không thể có tất cả :)).
Điều đáng kinh ngạc

11

Bạn có thể thêm chú thích [Phân biệt chữ hoa chữ thường] cho EF6 + Code-first

Thêm lớp học này

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class CaseSensitiveAttribute : Attribute
{
    public CaseSensitiveAttribute()
    {
        IsEnabled = true;
    }
    public bool IsEnabled { get; set; }
}

public class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(AlterColumnOperation alterColumnOperation)
    {
        base.Generate(alterColumnOperation);
        AnnotationValues values;
        if (alterColumnOperation.Column.Annotations.TryGetValue("CaseSensitive", out values))
        {
            if (values.NewValue != null && values.NewValue.ToString() == "True")
            {
                using (var writer = Writer())
                {
                    //if (System.Diagnostics.Debugger.IsAttached == false) System.Diagnostics.Debugger.Launch();

                    // https://github.com/mono/entityframework/blob/master/src/EntityFramework.SqlServer/SqlServerMigrationSqlGenerator.cs
                    var columnSQL = BuildColumnType(alterColumnOperation.Column); //[nvarchar](100)
                    writer.WriteLine(
                        "ALTER TABLE {0} ALTER COLUMN {1} {2} COLLATE SQL_Latin1_General_CP1_CS_AS {3}",
                        alterColumnOperation.Table,
                        alterColumnOperation.Column.Name,
                        columnSQL,
                        alterColumnOperation.Column.IsNullable.HasValue == false || alterColumnOperation.Column.IsNullable.Value == true ? " NULL" : "NOT NULL" //todo not tested for DefaultValue
                        );
                    Statement(writer);
                }
            }
        }
    }
}

public class CustomApplicationDbConfiguration : DbConfiguration
{
    public CustomApplicationDbConfiguration()
    {
        SetMigrationSqlGenerator(
            SqlProviderServices.ProviderInvariantName,
            () => new CustomSqlServerMigrationSqlGenerator());
    }
}

Sửa đổi DbContext của bạn, thêm

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<CaseSensitiveAttribute, bool>(
                "CaseSensitive",
                (property, attributes) => attributes.Single().IsEnabled));
        base.OnModelCreating(modelBuilder);
    }

Sau đó làm

Add-Migration CaseSensitive

Cập nhật cơ sở dữ liệu

dựa trên bài viết https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ với một số sửa lỗi


11

WHEREcác điều kiện trong SQL Server không phân biệt chữ hoa chữ thường theo mặc định. Làm cho nó phân biệt chữ hoa chữ thường bằng cách thay đổi các đối chiếu mặc định của cột ( SQL_Latin1_General_CP1_CI_AS) thành SQL_Latin1_General_CP1_CS_AS.

Cách dễ dàng để làm điều này là với mã. Thêm tệp di chuyển mới và sau đó thêm tệp này vào bên trong Upphương thức:

public override void Up()
{
   Sql("ALTER TABLE Thingies ALTER COLUMN Name VARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL");
}

Nhưng

Bạn có thể tạo chú thích tùy chỉnh được gọi là "Phân biệt chữ hoa chữ thường" bằng cách sử dụng các tính năng EF6 mới và bạn có thể trang trí các thuộc tính của mình như sau:

[CaseSensitive]
public string Name { get; set; }

Bài đăng trên blog này giải thích cách làm điều đó.


Trong bài viết đó có một lỗi
RouR

3

Câu trả lời do @Morteza Manavi đưa ra sẽ giải quyết được vấn đề. Tuy nhiên, đối với giải pháp phía máy khách , một cách thanh lịch sẽ là như sau (thêm kiểm tra kỹ).

var firstCheck = Thingies.Where(t => t.Name == "ThingamaBob")
    .FirstOrDefault();
var doubleCheck = (firstCheck?.Name == model.Name) ? Thingies : null;

-4

Tôi thích câu trả lời của Morteza và thường muốn sửa ở phía máy chủ. Đối với phía máy khách, tôi thường sử dụng:

Dim bLogin As Boolean = False

    Dim oUser As User = (From c In db.Users Where c.Username = UserName AndAlso c.Password = Password Select c).SingleOrDefault()
    If oUser IsNot Nothing Then
        If oUser.Password = Password Then
            bLogin = True
        End If
    End If

Về cơ bản, trước tiên hãy kiểm tra xem có người dùng với các tiêu chí bắt buộc hay không, sau đó kiểm tra xem mật khẩu có giống nhau không. Hơi dài dòng một chút, nhưng tôi cảm thấy nó dễ đọc hơn khi có thể có một loạt các tiêu chí liên quan.


2
Câu trả lời này ngụ ý rằng bạn đang lưu trữ mật khẩu dưới dạng văn bản thuần túy trong cơ sở dữ liệu của mình, đây là một lỗ hổng bảo mật lớn.
Jason Coyne,

2
@JasonCoyne Mật khẩu anh được so sánh với đã có thể được băm
Peter Morris

-4

Cả hai đều không StringComparison.IgnoreCaselàm việc cho tôi. Nhưng điều này đã làm:

context.MyEntities.Where(p => p.Email.ToUpper().Equals(muser.Email.ToUpper()));

2
Điều này sẽ không giúp giải quyết những câu hỏi đã được, đó là,How can I achieve case sensitive comparison
Reg Sửa

-4

Sử dụng string.Equals

Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCulture);

Ngoài ra, bạn không phải lo lắng về null và chỉ nhận lại thông tin bạn muốn.

Sử dụng StringComparision.CurrentCultureIgnoreCase cho Phân biệt chữ hoa chữ thường.

Thingies.First(t => string.Equals(t.Name, "ThingamaBob", StringComparison.CurrentCultureIgnoreCase);

Equals () không thể được chuyển đổi thành SQL ... Ngoài ra, nếu bạn thử và sử dụng phương thức instance, StringComparison sẽ bị bỏ qua.
LMK

Bạn đã thử giải pháp này chưa? Tôi đã thử điều này cuối cùng và hoạt động tốt với EF.
Darshan Joshi

-6

Không chắc chắn về EF4, nhưng EF5 hỗ trợ điều này:

Thingies
    .First(t => t.Name.Equals(
        "ThingamaBob",
        System.StringComparison.InvariantCultureIgnoreCase)

Tò mò những gì sql tạo ra.
Ronnie Overby

Tôi đã kiểm tra điều này với EF5, nó chỉ đơn giản tạo ra WHERE ... = ... trong SQL. Vì vậy, một lần nữa, điều này phụ thuộc vào cài đặt đối chiếu ở phía máy chủ SQL.
Achim

Ngay cả với một đối chiếu phân biệt chữ hoa chữ thường trong DB, tôi không thể lấy cái này hoặc bất kỳ cái nào khác StringComparisontạo ra sự khác biệt. Tôi đã thấy đủ người đề xuất loại điều này nên hoạt động để nghĩ rằng vấn đề nằm ở đâu đó trong tệp EDMX (db-first), mặc dù stackoverflow.com/questions/841226/…
drzaus
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.