LINQ to Entities 대소 문자 구분 비교


115

LINQ to Entities에서는 대 / 소문자를 구분하지 않습니다.

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

LINQ to Entities와 대 / 소문자 구분 비교를 수행하려면 어떻게해야합니까?


@Ronnie : 확실합니까? 대소 문자를 구분하지 않는 비교 를 의미 합니까?
Michael Petrotta 2010 년

14
물론입니다. 아니, 그런 뜻이 아닙니다.
Ronnie

12
아니요, EF 4.0 (SQL Server 2008 R2 포함)을 실행하는 컴퓨터에서 위의 내용은 대소 문자를 구분하지 않습니다. 많은 곳에서 EF가 기본 대소 문자를 구분한다고 말하는 것을 알고 있지만 그것은 내가 경험 한 것이 아닙니다.
tster

3
기본 데이터베이스에 의존하지 않습니까?
codymanix 2010 년

1
@codymanix : 좋은 질문입니다! Linq에서 EF로 DB 쿼리에 대한 람다 식을 변환합니까? 나는 답을 모른다.
Tergiver

답변:


163

궁극적으로 Lambda 식을 SQL 문으로 변환하는 LINQ To Entities 를 사용하고 있기 때문 입니다. 즉, 대소 문자 구분은 기본적으로 SQL_Latin1_General_CP1_CI_AS 데이터 정렬이 있고 대소 문자를 구분하지 않는 SQL Server의 자비에 있습니다 .

ObjectQuery.ToTraceString 을 사용하여 실제로 SQL Server에 제출 된 생성 된 SQL 쿼리를 확인하면 수수께끼가 드러납니다.

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

LINQ to Entities 쿼리 를 만들 때 LINQ to Entities 는 LINQ 파서를 활용하여 쿼리 처리를 시작하고이를 LINQ 식 트리로 변환합니다. 그런 다음 LINQ 식 트리는 식 트리를 명령 트리로 변환하는 개체 서비스 API 로 전달됩니다 . 그런 다음 명령 트리를 기본 데이터베이스 명령 텍스트로 변환하는 저장소 공급자 (예 : SqlClient)로 전송됩니다. 쿼리는 데이터 저장소에서 실행되고 결과는 개체 서비스에 의해 엔티티 개체구체화 됩니다.. 대소 문자 구분을 고려하는 논리가 없습니다. 따라서 조건 자에 어떤 대소 문자를 입력하든 해당 열에 대한 SQL Server 데이터 정렬을 변경하지 않는 한 SQL Server는 항상 동일하게 처리합니다.

서버 측 솔루션 :

따라서 가장 좋은 해결책은 Thingies 테이블 의 Name 열 데이터 정렬을 SQL Server에서 실행하여 대소 문자를 구분하는 COLLATE Latin1_General_CS_AS로 변경 하는 것입니다.

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

SQL Server Collate 에 대한 자세한 내용 은 SQL SERVER Collate 대소 문자 구분 SQL 쿼리 검색을 참조하십시오.

클라이언트 측 솔루션 :

클라이언트 측에 적용 할 수있는 유일한 솔루션은 LINQ to Objects 를 사용 하여 그다지 우아하지 않은 또 다른 비교를 수행하는 것입니다.

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

Entity Framework로 데이터베이스 스키마를 생성하고 있으므로 호출 코드를 사용하는 솔루션이 가장 좋습니다. 결과가 나오면 체크를 할 것 같아요. 감사.
Ronnie Overby

문제 없어요. 예, 맞습니다. 클라이언트 측 솔루션으로 답변을 업데이트했지만 그다지 우아하지 않으며 여전히 데이터 저장소 솔루션을 사용하는 것이 좋습니다.
Morteza Manavi 2010 년

18
@eglasius 이것은 완전히 사실이 아닙니다. 모든 데이터를 가져 오지 않고 대소 문자를 구분하지 않고 일치하는 데이터 만 가져온 다음 클라이언트 대소 문자를 구분하여 다시 필터링합니다. 물론 대소 문자를 구분하지 않고 일치하는 수천 개의 항목이 있지만 그중 하나만 대소 문자를 구분하는 올바른 항목이면 많은 오버 헤드가 발생합니다. 하지만 현실이 그런 시나리오를 제시 할 것이라고는 생각하지 않습니다 ... :)
Achim

1
@MassoodKhaari 당신이 게시 한 솔루션은 비교의 양쪽을 낮추기 때문에 대소 문자를 구분하지 않습니다. OP는 대소 문자 구분 비교가 필요합니다.
Jonny

1
"그러므로 최선의 해결책은 Thingies 테이블의 Name 열의 데이터 정렬을 COLLATE Latin1_General_CS_AS로 변경하는 것입니다."-이것이 최선이라고 생각하지 않습니다. 대부분의 경우 대소 문자를 구분하지 않는 LIKE 필터 (.Contains ())가 필요하지만 때로는 대소 문자를 구분해야합니다. "클라이언트 측 솔루션"을 사용해 보겠습니다. 제 사용 사례에 비해 훨씬 더 우아합니다 (그게 무엇을하는지 이해하면 좋겠지 만 모든 것을 가질 수는 없습니다 :)).
놀라운 1

11

EF6 + 코드 우선에 대한 [CaseSensitive] 주석을 추가 할 수 있습니다.

이 수업 추가

[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 수정, 추가

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

그런 다음

추가 마이그레이션 CaseSensitive

데이터베이스 갱신

https://milinaudara.wordpress.com/2015/02/04/case-sensitive-search-using-entity-framework-with-custom-annotation/ 기사를 기반으로 일부 버그 수정


11

WHERESQL Server의 조건은 기본적으로 대소 문자를 구분하지 않습니다. 열의 기본 데이터 정렬 ( SQL_Latin1_General_CP1_CI_AS)을 로 변경하여 대소 문자를 구분 합니다 SQL_Latin1_General_CP1_CS_AS.

이를 수행하는 깨지기 쉬운 방법은 코드를 사용하는 것입니다. 새 마이그레이션 파일을 추가 한 다음 Up메서드 내부에 추가합니다 .

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

그러나

새로운 EF6 기능을 사용하여 "CaseSensitive"라는 사용자 지정 주석을 만들 수 있으며 다음과 같이 속성을 장식 할 수 있습니다.

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

블로그 게시물 은이를 수행하는 방법을 설명합니다.


그 기사에 버그가 있습니다
RouR

3

@Morteza Manavi의 답변이 문제를 해결합니다. 그래도 클라이언트 측 솔루션 의 경우 우아한 방법은 다음과 같습니다 (이중 확인 추가).

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

-4

나는 Morteza의 대답을 좋아했으며 일반적으로 서버 측에서 수정하는 것을 선호합니다. 클라이언트 측의 경우 일반적으로 다음을 사용합니다.

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

기본적으로 필요한 기준을 가진 사용자가 있는지 먼저 확인한 다음 암호가 동일한 지 확인하십시오. 약간 장황하지만 많은 기준이 관련되어있을 때 읽기가 더 쉽다고 생각합니다.


2
이 답변은 데이터베이스에 암호를 일반 텍스트로 저장하고 있음을 의미하며 이는 엄청난 보안 취약점입니다.
Jason Coyne

2
그는 이미 해시 수와 비교되는 암호 @JasonCoyne
피터 모리스

-4

어느 쪽도 StringComparison.IgnoreCase나를 위해 일 하지 않았습니다 . 그러나 이것은 :

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

2
이것은 질문에 도움이되지 않을 것입니다.How can I achieve case sensitive comparison
Reg Edit

-4

string.Equals 사용

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

또한 null에 대해 걱정할 필요가 없으며 원하는 정보 만 얻을 수 있습니다.

대소 문자를 구분하지 않으려면 StringComparision.CurrentCultureIgnoreCase를 사용하십시오.

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

Equals ()는 SQL로 변환 할 수 없습니다 ... 또한 인스턴스 메서드를 사용하려고하면 StringComparison이 무시됩니다.
LMK

이 솔루션을 사용해 보셨습니까? 나는 EF로 잘 일하면서 이것을 시도했습니다.
Darshan Joshi

-6

EF4에 대해서는 확실하지 않지만 EF5는 다음을 지원합니다.

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

어떤 SQL을 생성하는지 궁금합니다.
Ronnie Overby

나는 이것을 EF5로 확인했고 단순히 SQL에서 WHERE ... = ...를 생성했습니다. 다시 말하지만 이것은 SQL 서버 측의 데이터 정렬 설정에 따라 다릅니다.
Achim

DB에서 대소 문자를 구분하는 데이터 정렬을 사용하더라도 StringComparison차이를 만들기 위해이 열거 형 이나 다른 열거 형을 가져올 수 없습니다 . 나는 이런 종류의 일이 EDMX 파일 (db-first) 어딘가에 문제가 있다고 생각할 수 있다고 제안하는 사람들을 충분히 보았지만 stackoverflow.com/questions/841226/…
drzaus
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.