LINQ to Entities büyük / küçük harfe duyarlı karşılaştırma


115

Bu, LINQ to Entities'deki büyük / küçük harfe duyarlı bir karşılaştırma değildir:

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

LINQ to Entities ile büyük / küçük harfe duyarlı karşılaştırmayı nasıl yapabilirim?


@Ronnie: Bundan emin misin? Büyük / küçük harfe duyarlı olmayan karşılaştırmayı mı kastediyorsunuz ?
Michael Petrotta

14
Kesinlikle emin. Hayır öyle demek istemiyorum.
Ronnie Overby

12
Hayır, SQL Server 2008 R2 ile EF 4.0 çalıştıran bilgisayarımda yukarıdakiler büyük / küçük harfe duyarlı değildir. Pek çok yerin EF'nin varsayılan büyük / küçük harfe duyarlı olduğunu söylediğini biliyorum, ancak deneyimlediğim şey bu değil.
tster

3
Bu, temel veritabanına bağlı olmayacak mı?
codymanix

1
@codymanix: Bu iyi bir soru! Linq'den EF'ye bir DB sorgusu için lambda ifadesini çevirir mi? Cevabı bilmiyorum
Tergiver

Yanıtlar:


163

Bunun nedeni , sonuçta Lambda ifadelerinizi SQL ifadelerine dönüştüren LINQ To Entities kullanıyor olmanızdır . Bu, büyük / küçük harf duyarlılığının varsayılan olarak SQL_Latin1_General_CP1_CI_AS Harmanlamasına sahip olan ve büyük / küçük harfe duyarlı OLMAYAN SQL Sunucunuzun insafına kaldığı anlamına gelir .

Aslında SQL Server'a gönderilen oluşturulan SQL sorgusunu görmek için ObjectQuery.ToTraceString kullanmak gizemi ortaya çıkarır:

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

Bir LINQ to Entities sorgusu oluşturduğunuzda, LINQ to Entities , sorguyu işlemeye başlamak için LINQ ayrıştırıcısını kullanır ve bunu bir LINQ ifade ağacına dönüştürür. LINQ ifade ağacı daha sonra ifade ağacını bir komut ağacına dönüştüren Nesne Hizmetleri API'sine aktarılır . Daha sonra, komut ağacını yerel veritabanı komut metnine dönüştüren mağaza sağlayıcısına (örn. SqlClient) gönderilir. Veri deposu idam olsun sorgulamak ve sonuçları edilir materyalize içine Varlık Nesneleri tarafından Nesne Hizmetleri. Büyük / küçük harf duyarlılığını hesaba katmak için arasına hiçbir mantık konulmadı. Dolayısıyla yükleminize hangi durumu koyarsanız koyun, o sütun için SQL Server Harmanlamanızı değiştirmediğiniz sürece SQL Server tarafından her zaman aynı şekilde davranacaktır.

Sunucu tarafı çözümü:

Bu nedenle, en iyi çözüm , Thingies tablosundaki Ad sütununun harmanlamasını, SQL Server'ınızda bunu çalıştırarak büyük / küçük harfe duyarlı olan COLLATE Latin1_General_CS_AS olarak değiştirmek olacaktır :

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

SQL Server Harmanlamaları hakkında daha fazla bilgi için, SQL SUNUCUSU Harmanlama Büyük / Küçük Harfe Duyarlı SQL Sorgu Aramasına bir göz atın

İstemci tarafı çözüm:

İstemci tarafında uygulayabileceğiniz tek çözüm, çok zarif görünmeyen başka bir karşılaştırma yapmak için LINQ to Objects'i kullanmaktır :

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

Entity Framework ile veritabanı şeması oluşturuyorum, bu nedenle çağıran kodumu kullanan bir çözüm en iyisi olacaktır. Sonuçlar geldikten sonra sanırım bir kontrol yapacağım. Teşekkürler.
Ronnie Overby

Sorun değil. Evet, bu doğru ve cevabımı bir istemci tarafı çözümle güncelledim, ancak çok şık değil ve yine de veri deposu çözümünü kullanmanızı tavsiye ediyorum.
Murtaza Manavi

18
@eglasius Bu tam olarak doğru değildir: TÜM verileri getirmez, yalnızca büyük / küçük harfle eşleşen verileri duyarsız olarak alır ve bundan sonra müşteri durumunda yeniden hassas bir şekilde filtrelenir. Elbette, büyük / küçük harfe duyarlı olmayan binlerce girişiniz varsa, ancak bunlardan yalnızca biri doğru ve büyük / küçük harfe duyarlıysa, bu çok fazla ek yük anlamına gelir. Ama gerçekliğin bu tür senaryolar sunacağını sanmıyorum ... :)
Achim

1
@MassoodKhaari Gönderdiğiniz çözüm, onu Büyük / Küçük Harfe Duyarsız hale getirecektir çünkü karşılaştırmanın her iki tarafını da alt sıraya koyuyorsunuz. OP'nin büyük / küçük harfe duyarlı bir karşılaştırmaya ihtiyacı vardır.
Jonny

1
"Bu nedenle, en iyi çözüm Thingies tablosundaki Ad sütununun harmanlamasını COLLATE Latin1_General_CS_AS olarak değiştirmek olacaktır" - Bunun en iyisi olduğunu düşünmüyorum. Çoğu zaman büyük / küçük harfe duyarlı olmayan LIKE filtresine (.Contains ()) ihtiyacım var, ancak bazen büyük / küçük harfe duyarlı olması gerekir. "İstemci tarafı çözümünüzü" deneyeceğim - bence kullanım durumum için çok daha zarif (ne yaptığını anlamak güzel olurdu ama hepsine sahip olamazsınız :)).
İnanılmaz Ocak

11

EF6 + Code-first için [CaseSensitive] ek açıklaması ekleyebilirsiniz

Bu sınıfları ekle

[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());
    }
}

DbContext'inizi değiştirin, ekleyin

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

O zaman yap

Add-Migration CaseSensitive

Veri tabanını güncelle

bazı hata düzeltmeleri ile https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ makalesine göre


11

WHERESQL Server'daki koşullar varsayılan olarak büyük / küçük harf duyarlıdır. Sütunun varsayılan harmanlamalarını ( SQL_Latin1_General_CP1_CI_AS) olarak değiştirerek büyük / küçük harfe duyarlı hale getirin SQL_Latin1_General_CP1_CS_AS.

Bunu yapmanın kırılgan yolu kodlamadır. Yeni bir geçiş dosyası ekleyin ve ardından bunu Upyöntemin içine ekleyin :

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

Fakat

Yeni EF6 özelliklerini kullanarak "CaseSensitive" adlı özel açıklama oluşturabilir ve özelliklerinizi şu şekilde dekore edebilirsiniz:

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

Bu blog yazısı bunun nasıl yapılacağını açıklıyor.


Bu makalede bir hata var
RouR

3

@Morteza Manavi'nin verdiği cevap sorunu çözüyor. Yine de, bir istemci tarafı çözümü için , zarif bir yol şu olacaktır (bir çift kontrol eklemek).

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

-4

Morteza'nın cevabını beğendim ve normalde sunucu tarafında düzeltmeyi tercih ederim. İstemci tarafı için normalde kullanıyorum:

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

Temel olarak, önce gerekli kriterlere sahip bir kullanıcı olup olmadığını kontrol edin, ardından şifrenin aynı olup olmadığını kontrol edin. Biraz uzun soluklu, ama bir sürü kriter söz konusu olduğunda okumanın daha kolay olduğunu hissediyorum.


2
Bu cevap, şifreleri veritabanınızda düz metin olarak sakladığınızı ve bu da büyük bir güvenlik açığı olduğunu gösterir.
Jason Coyne

2
@JasonCoyne Karşılaştırmakta olduğu şifre çoktan hash edilmiş olabilir
Peter Morris

-4

Hiçbiri StringComparison.IgnoreCasebenim için çalışmadı. Ama bu yaptı:

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

2
Bu, sorulan soruya yardımcı olmazdıHow can I achieve case sensitive comparison
Reg Edit

-4

String.Equals kullanın

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

Ayrıca, null konusunda endişelenmenize ve yalnızca istediğiniz bilgileri geri almanıza gerek yoktur.

Büyük / Küçük Harfe Duyarsız için StringComparision.CurrentCultureIgnoreCase kullanın.

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

Equals (), SQL'e dönüştürülemez ... Ayrıca örnek yöntemini denerseniz ve kullanırsanız, StringComparison yoksayılır.
LMK

Bu çözümü denedin mi? Bunu EF ile iyi çalışarak sonunda denedim.
Darshan Joshi

-6

EF4 hakkında emin değilim, ancak EF5 bunu destekliyor:

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

Hangi sql oluşturduğunu merak ediyorum.
Ronnie Overby

Bunu EF5 ile kontrol ettim, basitçe SQL'de bir WHERE ... = ... oluşturdu. Yani yine, bu SQL sunucusu tarafındaki harmanlama ayarlarına bağlıdır.
Achim

DB'de büyük / küçük harfe duyarlı bir harmanlama olsa bile, bunu veya diğer StringComparisonnumaralandırmaların bir fark yaratmasını sağlayamadım . Sorunun EDMX dosyasında (db-ilk) bir yerde olduğunu düşünmek için bu tür bir şeyin işe yaraması gerektiğini öneren yeterince insan gördüm, ancak stackoverflow.com/questions/841226/…
drzaus
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.