Linq to Entities - Mệnh đề SQL IN IN


230

Trong T-SQL, bạn có thể có một truy vấn như:

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

Làm thế nào bạn có thể sao chép điều đó trong truy vấn LINQ to Entities? Nó thậm chí có thể?

Câu trả lời:


349

Bạn cần bật nó lên theo cách bạn nghĩ về nó. Thay vì thực hiện "vào" để tìm quyền người dùng của mặt hàng hiện tại trong một bộ quyền người dùng được xác định trước, bạn đang hỏi một bộ quyền người dùng được xác định trước nếu nó có chứa giá trị áp dụng của mặt hàng hiện tại. Điều này chính xác giống như cách bạn sẽ tìm thấy một mục trong danh sách thông thường trong .NET.

Có hai cách để thực hiện việc này bằng LINQ, một cách sử dụng cú pháp truy vấn và cách khác sử dụng cú pháp phương thức. Về cơ bản, chúng giống nhau và có thể được sử dụng thay thế cho nhau tùy theo sở thích của bạn:

Cú pháp truy vấn:

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;
}

Phương pháp Cú pháp:

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

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

Sở thích cá nhân của tôi trong trường hợp này có thể là cú pháp phương thức vì thay vì gán biến, tôi có thể thực hiện foreach qua một cuộc gọi ẩn danh như thế này:

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

Về mặt cú pháp, điều này có vẻ phức tạp hơn và bạn phải hiểu khái niệm biểu thức lambda hoặc đại biểu để thực sự tìm hiểu điều gì đang xảy ra, nhưng như bạn có thể thấy, điều này làm cô đọng một số tiền hợp lý.

Tất cả đều thuộc về phong cách và sở thích mã hóa của bạn - cả ba ví dụ của tôi đều làm điều tương tự hơi khác nhau.

Một cách khác thậm chí không sử dụng LINQ, bạn có thể sử dụng cú pháp phương thức tương tự thay thế "where" bằng "FindAll" và nhận được kết quả tương tự, cũng sẽ hoạt động trong .NET 2.0:

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

có thể tôi đã quá nhanh để đánh dấu là câu trả lời, nhưng tôi không nhận được. Tiếp theo sau {"Quản trị viên", "Người dùng", "Hạn chế"} VS2008 không thích mã đó một chút.
StevenMcD

1
đúng với tên của tôi "FailBoy" Tôi đã hiểu ra: PI đặt vào một chuỗi [] và sau đó sử dụng nó và nó hoạt động. Cảm ơn!
StevenMcD

xin lỗi, tôi đã quên làm mới mảng ẩn danh;) Tôi đã sửa ví dụ mã của mình. Vui vì bạn đã tìm ra nó trên mặc dù.
BenAlabaster

28
Câu trả lời này đã đúng khi có câu hỏi về Linq-to-SQL hoặc Linq nói chung. Tuy nhiên, vì nó đặc biệt nói "Linq-to-Entities", câu trả lời này không chính xác. mảng.Contains không (chưa) được hỗ trợ bởi Linq-to-Entities.
KristoferA

6
@KristoferA - điều đó có thể đúng với các phiên bản trước của EF, nhưng đối với tôi với EF4 thì có vẻ tốt.
Drew Noakes

21

Điều này sẽ đủ mục đích của bạn. Nó so sánh hai bộ sưu tập và kiểm tra xem một bộ sưu tập có các giá trị khớp với các bộ sưu tập trong bộ sưu tập khác không

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

9

Nếu bạn đang sử dụng VS2008 / .net 3.5, hãy xem mẹo số 8 của Alex James: http://bloss.msdn.com/alexj/archive/2009/03/26/tip-8-wr-where-in-style -queries-using-linq-to-entity.aspx

Nếu không, chỉ sử dụng phương thức Array.Contains (someEntity.Member).


Vui lòng tránh sử dụng các câu trả lời chỉ liên kết, bạn nên tóm tắt nội dung của liên kết trong câu trả lời của bạn, trong trường hợp liên kết bị hỏng trong tương lai.


8

Tôi sẽ tham gia Nội tại trong bối cảnh này. Nếu tôi đã sử dụng có chứa, nó sẽ lặp lại 6 lần mặc dù thực tế là chỉ có một trận đấu.

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

Nhược điểm của Chứa

Giả sử tôi có hai đối tượng danh sách.

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

Sử dụng Chứa, nó sẽ tìm kiếm từng mục Danh sách 1 trong Danh sách 2 có nghĩa là việc lặp lại sẽ xảy ra 49 lần !!!


5
Điều này hoàn toàn bỏ qua thực tế là câu lệnh được dịch sang SQL. Xem tại đây .
Gert Arnold

5

Đây có thể là cách khả thi để bạn có thể trực tiếp sử dụng các phương thức mở rộng LINQ để kiểm tra mệnh đề trong

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

2

Tôi cũng đã cố gắng làm việc với một thứ giống như SQL-IN - truy vấn theo Mô hình dữ liệu thực thể . Cách tiếp cận của tôi là một trình xây dựng chuỗi để soạn một biểu thức OR lớn. Điều đó thật tồi tệ, nhưng tôi sợ đó là cách duy nhất để đi ngay bây giờ.

Bây giờ, có vẻ như thế này:

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

Làm việc với GUID trong ngữ cảnh này : Như bạn có thể thấy ở trên, luôn có từ "GUID" trước chính GUID trong các đoạn chuỗi truy vấn. Nếu bạn không thêm cái này, hãy ObjectQuery<T>.Whereném ngoại lệ sau:

Các loại đối số 'Edm.Guid' và 'Edm.String' không tương thích với thao tác này., Gần biểu thức bằng, dòng 6, cột 14.

Tìm thấy điều này trong Diễn đàn MSDN, có thể hữu ích để ghi nhớ.

Matthias

... mong chờ phiên bản tiếp theo của .NET và Entity Framework, khi mọi thứ trở nên tốt hơn. :)


2

Một phương pháp khác để trả lời BenAlabaster

Trước hết, bạn có thể viết lại truy vấn như thế này:

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

Chắc chắn điều này là 'nhiều từ' và một nỗi đau để viết nhưng nó hoạt động như nhau.

Vì vậy, nếu chúng ta có một số phương thức tiện ích giúp dễ dàng tạo các loại biểu thức LINQ này, chúng ta sẽ kinh doanh.

với một phương thức tiện ích tại chỗ, bạn có thể viết một cái gì đó như thế này:

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

Điều này xây dựng một biểu thức có tác dụng tương tự như:

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

Nhưng điều quan trọng hơn thực sự hoạt động đối với .NET 3.5 SP1.

Đây là chức năng hệ thống ống nước làm cho điều này có thể:

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

Tôi sẽ không cố gắng giải thích phương thức này, ngoài việc nói rằng về cơ bản nó xây dựng một biểu thức vị ngữ cho tất cả các giá trị bằng cách sử dụng valueSelector (tức là p => p.User_Rights) và OR các vị từ đó cùng nhau tạo ra một biểu thức cho hoàn thành Thuộc tính

Nguồn: http://bloss.msdn.com/b/alexj/archive/2009/03/26/tip-8-wr-where-in-style-queries-USE-linq-to-entities.aspx


0

Ví dụ thực tế:

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;

-14

Nghiêm túc? Bạn chưa bao giờ sử dụng

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

9
-1 Hãy thử điều này với 20 giá trị trở lên trong một bảng có hơn 1000 hàng và bạn sẽ nhanh chóng thấy được lợi thế của giải pháp được chấp nhận. Ngoài ra, không dễ để thêm một số điều kiện tùy ý vào câu lệnh where (như nếu người dùng đang chọn để bao gồm tùy chọn 1 và 2, nhưng không phải 3).
Đã xem

Chà, tôi không cần bất kỳ thứ gì của nhà khoa học điên và câu trả lời này được bỏ phiếu vì tôi cần AND và 2 ORS var SamplePoints = (từ c trong _db.tblPWS_WSFinksID_ISN_Lookup.OrderBy (x => x.WSFStateCode) PWS == id && ((c.WSFStateCode.Subopes (0, 2) == "SR") || (c.WSFStateCode.Sub chuỗi (0, 2) == "CH")) chọn c) .ToList () ;
JustJohn

@Trisped - số lượng hàng (1000) không thay đổi bất cứ điều gì - hoặc tôi có thiếu gì không?
tymtam

@Tymski Có, số lượng hàng quan trọng. Càng nhiều hàng, tính toán càng nhiều. Tương tự với số lượng giá trị có thể : Checks = NumValues * NumRows. Bởi vì đây là phép tính loại M * N, nếu một trong hai là nhỏ thì thời gian để thực hiện mỗi kiểm tra cần thiết cũng sẽ nhỏ. Tôi đã thêm các ràng buộc để cjm30305 biết cách thiết lập môi trường thử nghiệm trong đó cho thấy lý do tại sao giải pháp của anh ta kém.
Đã xem

@Trisped Bạn có nói rằng where new[] { 1, 2, 3 }.Contains(x)ít so sánh hơn where (x == 1 || x == 2 || x == 3)không?
tymtam
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.