Linq- 엔티티-SQL“IN”절


230

T-SQL에서는 다음과 같은 쿼리를 가질 수 있습니다.

SELECT * FROM Users WHERE User_Rights IN ("Admin", "User", "Limited")

LINQ to Entities 쿼리에서 어떻게 복제합니까? 가능합니까?

답변:


349

당신이 생각하는 방식으로 머리를 켜야합니다. 사전 정의 된 적용 가능한 사용자 권한 세트에서 현재 항목의 사용자 권한을 찾기 위해 "입력"을 수행하는 대신 현재 항목의 해당 값이 포함되어 있는지 사전 정의 된 사용자 권한 세트를 요구합니다. 이것은 .NET의 일반 목록에서 항목을 찾는 것과 정확히 같은 방법입니다.

LINQ를 사용하여이 작업을 수행하는 방법에는 두 가지가 있습니다. 하나는 쿼리 구문을 사용하고 다른 하나는 메서드 구문을 사용합니다. 본질적으로 그것들은 동일하며 당신의 선호에 따라 상호 교환 가능하게 사용될 수 있습니다 :

쿼리 구문 :

var selected = from u in users
               where new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights)
               select u

foreach(user u in selected)
{
    //Do your stuff on each selected user;
}

방법 구문 :

var selected = users.Where(u => new[] { "Admin", "User", "Limited" }.Contains(u.User_Rights));

foreach(user u in selected)
{
    //Do stuff on each selected user;
}

변수를 할당하는 대신 다음과 같은 익명 호출을 통해 foreach를 수행 할 수 있기 때문에이 인스턴스에서 개인적으로 선호하는 방법은 구문입니다.

foreach(User u in users.Where(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
    //Do stuff on each selected user;
}

문법적으로 이것은 더 복잡해 보이며 실제로 무슨 일이 일어나고 있는지 파악하기 위해 람다 식 또는 대리자의 개념을 이해해야하지만, 알 수 있듯이 이것은 코드를 상당량 압축합니다.

그것은 모두 코딩 스타일과 선호도에 달려 있습니다. 내 세 가지 예제 모두 약간 다르게 똑같은 일을합니다.

다른 방법으로는 LINQ를 사용하지 않고 "where"를 "FindAll"로 바꾸는 동일한 방법 구문을 사용하고 동일한 결과를 얻을 수 있습니다.이 결과는 .NET 2.0에서도 작동합니다.

foreach(User u in users.FindAll(u => new [] { "Admin", "User", "Limited" }.Contains(u.User_Rights)))
{
    //Do stuff on each selected user;
}

어쩌면 나는 대답으로 표시하기에 너무 빠르지 만 {..
StevenMcD

1
내 이름 "FailBoy"에 충실하다 나는 그것을 알아 냈다. : PI는 string []에 넣고 그것을 사용했다. 감사!
StevenMcD

죄송합니다, 익명 배열을 새로 만드는 것을 잊었습니다.) 코드 예제를 수정했습니다. 그래도 알아서 다행입니다.
BenAlabaster

28
Linq-to-SQL 또는 Linq에 대한 일반적인 질문에 대해서는이 답변이 정확했을 것입니다. 그러나 특히 "Linq-to-Entities"라고 표시되어 있기 때문에이 답변은 잘못되었습니다. Linq-to-Entities가 (아직) 지원하지 않습니다.
KristoferA

6
@KristoferA-그것은 이전 버전의 EF에서는 사실 일지 모르지만 EF4에서는 나에게 괜찮습니다.
Drew Noakes

21

이것은 당신의 목적으로 충분할 것입니다. 두 컬렉션을 비교하고 한 컬렉션에 다른 컬렉션의 값과 일치하는 값이 있는지 확인합니다.

fea_Features.Where(s => selectedFeatures.Contains(s.feaId))

9

VS2008 / .net 3.5를 사용하는 경우 Alex James의 팁 # 8을 참조하십시오. http://blogs.msdn.com/alexj/archive/2009/03/26/tip-8-writing-where-in-style)을 -queries-using-linq-to-entities.aspx

그렇지 않으면 array.Contains (someEntity.Member) 메소드를 사용하십시오.


링크 전용 답변을 사용하지 마십시오. 나중에 링크가 끊어 질 경우 답변 내용에 링크 내용을 요약해야합니다.


8

이 맥락에서 Inner Join으로갑니다. 포함을 사용하면 일치하는 항목이 하나만 있음에도 불구하고 6 번 반복합니다.

var desiredNames = new[] { "Pankaj", "Garg" }; 

var people = new[]  
{  
    new { FirstName="Pankaj", Surname="Garg" },  
    new { FirstName="Marc", Surname="Gravell" },  
    new { FirstName="Jeff", Surname="Atwood" }  
}; 

var records = (from p in people join filtered in desiredNames on p.FirstName equals filtered  select p.FirstName).ToList(); 

포함의 단점

두 개의 목록 객체가 있다고 가정합니다.

List 1      List 2
  1           12
  2            7
  3            8
  4           98
  5            9
  6           10
  7            6

포함을 사용하면 목록 2에서 각 목록 1 항목을 검색하여 반복이 49 회 발생한다는 것을 의미합니다 !!!


5
이것은 명령문이 SQL로 변환된다는 사실을 완전히 무시합니다. 여기를 참조 하십시오 .
게르트 아놀드

5

이것은 LINQ 확장 메소드를 직접 사용하여 in 절을 확인할 수있는 가능한 방법 일 수 있습니다.

var result = _db.Companies.Where(c => _db.CurrentSessionVariableDetails.Select(s => s.CompanyId).Contains(c.Id)).ToList();

2

또한 Entity Data Model 에 대해 SQL-IN과 비슷한 것을 쿼리하려고했습니다 . 내 접근 방식은 큰 OR 식을 작성하는 문자열 작성기입니다. 정말 못 생겼지 만 지금 당장 갈 수있는 유일한 방법 인 것 같습니다.

이제 다음과 같이 보입니다.

Queue<Guid> productIds = new Queue<Guid>(Products.Select(p => p.Key));
if(productIds.Count > 0)
{
    StringBuilder sb = new StringBuilder();
    sb.AppendFormat("{0}.ProductId = Guid\'{1}\'", entities.Products.Name, productIds.Dequeue());
    while(productIds.Count > 0)
    {
        sb.AppendFormat(" OR {0}.ProductId = Guid\'{1}\'",
          entities.Products.Name, productIds.Dequeue());
    }
}

이 컨텍스트에서 GUID 작업 : 위에서 볼 수 있듯이 쿼리 문자열 조각에서 GUID 앞에 "GUID"라는 단어가 항상 있습니다. 이것을 추가하지 않으면 ObjectQuery<T>.Where다음 예외가 발생합니다.

인수 유형 'Edm.Guid'및 'Edm.String'은이 작업과 호환되지 않습니다. 거의 식 6, 열 14와 같습니다.

MSDN 포럼에서 이것을 발견하면 염두에 두는 것이 도움이 될 수 있습니다.

마티아스

... 모든 것이 더 나아질 때 .NET 및 Entity Framework의 다음 버전을 기대합니다. :)


2

BenAlabaster의 대안 대안

우선 다음과 같이 쿼리를 다시 작성할 수 있습니다.

var matches = from Users in people
        where Users.User_Rights == "Admin" ||
              Users.User_Rights == "Users" || 
              Users.User_Rights == "Limited"
        select Users;

확실히 이것은 더 '말랑 말랑한'이고 쓰는 것이 고통 스럽지만 모두 동일하게 작동합니다.

따라서 이런 종류의 LINQ 표현을 쉽게 만들 수있는 유틸리티 방법이 있다면 우리는 사업을 할 것입니다.

유틸리티 메소드를 사용하면 다음과 같이 작성할 수 있습니다.

var matches = ctx.People.Where(
        BuildOrExpression<People, string>(
           p => p.User_Rights, names
        )
);

이렇게하면 다음과 같은 효과가있는 표현식이 작성됩니다.

var matches = from p in ctx.People
        where names.Contains(p.User_Rights)
        select p;

그러나 더 중요한 것은 실제로 .NET 3.5 SP1에 대해 작동합니다.

이를 가능하게하는 배관 기능은 다음과 같습니다.

public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(
        Expression<Func<TElement, TValue>> valueSelector, 
        IEnumerable<TValue> values
    )
{     
    if (null == valueSelector) 
        throw new ArgumentNullException("valueSelector");

    if (null == values)
        throw new ArgumentNullException("values");  

    ParameterExpression p = valueSelector.Parameters.Single();

    if (!values.Any())   
        return e => false;

    var equals = values.Select(value =>
        (Expression)Expression.Equal(
             valueSelector.Body,
             Expression.Constant(
                 value,
                 typeof(TValue)
             )
        )
    );
   var body = equals.Aggregate<Expression>(
            (accumulate, equal) => Expression.Or(accumulate, equal)
    ); 

   return Expression.Lambda<Func<TElement, bool>>(body, p);
}

나는 본질적으로 valueSelector (예 : p => p.User_Rights)를 사용하여 모든 값에 대한 술어 표현식을 작성하고 그 술어를 함께 사용하여 완전한 표현식을 작성한다고 말하는 것 외에는이 방법을 설명하려고하지 않습니다. 술부

출처 : http://blogs.msdn.com/b/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx


0

실제 예 :

var trackList = Model.TrackingHistory.GroupBy(x => x.ShipmentStatusId).Select(x => x.Last()).Reverse();
List<int> done_step1 = new List<int>() {2,3,4,5,6,7,8,9,10,11,14,18,21,22,23,24,25,26 };
bool isExists = trackList.Where(x => done_step1.Contains(x.ShipmentStatusId.Value)).FirstOrDefault() != null;

-13

진심이야? 당신은 한 번도 사용하지 않은

where (t.MyTableId == 1 || t.MyTableId == 2 || t.MyTableId == 3)

9
-1 1000 개가 넘는 행이있는 테이블에서 20 개 이상의 값으로 이것을 시도하면 허용되는 솔루션의 이점을 빨리 볼 수 있습니다. 또한 where 문에 임의의 수의 조건을 추가하는 것은 쉽지 않습니다 (사용자가 옵션 1과 2를 포함하도록 선택하지만 3은 포함하지 않는 것처럼).
Trisped

글쎄, 나는 미친 과학자 재료가 필요하지 않았고 AND와 2 개의 ORS var SamplePoints = (c에서 _db.tblPWS_WSF_SPID_ISN_Lookup.OrderBy (x => x.WSFStateCode)가 필요했기 때문에이 대답은 내 투표에 나옵니다. PWS == id && ((c.WSFStateCode.Substring (0, 2) == "SR") || (c.WSFStateCode.Substring (0, 2) == "CH")) 선택 c) .ToList () ;
JustJohn

@Trisped-행 수 (1000)가 변경되지 않습니다. 또는 누락 된 것이 있습니까?
tymtam

@Tymski 네, 행 수는 중요합니다. 행이 많을수록 더 많은 계산이 이루어집니다. 가능한 값의 수와 같습니다 : Checks = NumValues * NumRows. 이것은 M * N 유형 계산이므로, 둘 중 하나라도 작 으면 필요한 각 검사를 수행하는 시간도 줄어 듭니다. cjm30305가 솔루션이 왜 좋지 않은지를 보여주는 테스트 환경을 설정하는 방법을 알 수 있도록 제약 조건을 추가했습니다.
Trisped

@Trisped 그렇다면 where new[] { 1, 2, 3 }.Contains(x)비교가 덜되는 것 where (x == 1 || x == 2 || x == 3)입니까?
tymtam
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.