2016-08-19 17 views
13

Tôi cần kiểm tra xem tất cả các định nghĩa có chứa một số dữ liệu cụ thể hay không. Nó hoạt động tốt ngoại trừ trường hợp khi GroupBy trả về bộ sưu tập trống.LINQ Tất cả trên bộ sưu tập trống

var exist = dbContext.Definitions 
        .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
        .GroupBy(x => x.PropertyTypeId) 
        .All(...some condition...); 

Làm thế nào để viết lại điều này để Tất cả sẽ trả về sai trên bộ sưu tập trống?

CẬP NHẬT: Đây là LINQ to SQL và tôi muốn thực thi điều này trong một cuộc gọi.

UPDATE2: Tôi nghĩ rằng công trình này:

var exist = dbContext.Definitions 
        .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
        .GroupBy(x => x.PropertyTypeId) 
        .Count(x => x 
         .All(...some condition...)) == propertyTypeIds.Count; 
+0

Đây có phải là LINQ to Objects hay cái gì khác không? Các câu trả lời có thể hoàn toàn khác nhau. –

+0

Hãy thử sử dụng AllOrDefault – elloco999

+1

'Tất cả' sẽ không trả về false trên một bộ sưu tập trống. Xem tại đây: http://stackoverflow.com/questions/7884888/why-does-enumerable-all-return-true-for-an-empty-sequence – sr28

Trả lời

0

gì về cách viết phương pháp khuyến nông của riêng bạn? (Tôi khá chắc chắn bạn sẽ đặt tên cho nó tốt hơn)

public static bool NotEmptyAll<T>(
    this IEnumerable<T> collection, 
    Func<T, bool> predicate) 
{ 
    return collection != null 
     && collection.Any() 
     && collection.All(predicate); 
} 

Sau đó gọi nó là thay vì All

var exist = definitions.Where(
     x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
     .GroupBy(x => x.PropertyTypeId) 
     .NotEmptyAll(
      ...some condition...)); 
+5

Lưu ý rằng điều này thực hiện truy vấn hai lần, đó cũng có thể là một ý tưởng tồi. –

+0

Cảm ơn bạn đã cảnh báo @JonSkeet. Nhưng đó là giải pháp duy nhất đi vào tâm trí của tôi. Vì nó không được gắn thẻ 'EntityFramework', tôi nghĩ rằng GENERALLY không nên tốn nhiều tiền. –

+0

Vâng, nó sẽ tốn gấp đôi so với khi thực hiện nó một lần ... nó cũng khá giống như ... không có toán tử LINQ thường xuyên nào đánh giá đầu vào của nó nhiều hơn một lần, IIRC. –

1

Vâng, bạn có thể làm điều đó trong hai bước:

var definitions = definitions.Where(
        x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
        .GroupBy(x => x.PropertyTypeId); 

var exist = definitions.Any() && definitions.All(...some condition...); 
+5

Không cần kiểm tra tính vô hiệu - nhưng bạn cần phải biết rằng điều này sẽ thực hiện truy vấn hai lần. –

+2

Nếu đây là một cái gì đó giống như Entity Framework, nó sẽ làm cho 2 truy vấn chạy trên cơ sở dữ liệu. – DavidG

+0

Đúng, nó sẽ thực hiện truy vấn hai lần. Cảm ơn bạn đã chỉ nó. – Fabjan

5

Bạn có thể có thể thực hiện việc này bằng cách sử dụng Aggregate, dọc theo các dòng:

.Aggregate(new {exists = 0, matches = 0}, (a, g) => 
     new {exists = a.exists + 1, matches = a.matches + g > 10 ? 1 : 0}) 

(Ở đây, g > 10 là thử nghiệm của tôi)

và logic sau đó đơn giản mà exists là lớn hơn không và rằng existsmatches có cùng giá trị.

Điều này tránh chạy toàn bộ truy vấn hai lần.

3

Bạn có thể sử dụng phương thức mở rộng DefaultIfEmpty và điều chỉnh some condition để đánh giá null đến false.

var exist = definitions 
    .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
    .GroupBy(x => x.PropertyTypeId) 
    .DefaultIfEmpty() 
    .All(...some condition...)); 
+1

Rất thông minh! Nói cách khác, '.All (g => g! = Null && (... một số điều kiện ...))'. Thêm dấu ngoặc đơn có thể không được yêu cầu tùy thuộc vào cách '... một số điều kiện ...' được viết. – devgeezer

1

Chỉnh sửa: câu trả lời đầu tiên sẽ không hoạt động.

Nếu bạn sắp xếp lại các truy vấn của bạn một chút, bạn có thể sử dụng DefaultIfEmpty mà không cần phải thay đổi tình trạng của bạn:

var exist = dbContext.Definitions 
        .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) 
            && x.CountryId == countryId) 
        .GroupBy(x => x.PropertyTypeId); 

      // apply the condition to all entries, 
      // resulting in sequence of bools (or empty), 
      // to permit the next step 
        .Select(...some condition...) 

      //if seq is empty, add `false` 
        .DefaultIfEmpty(false) 

      //All with identity function to apply the query and calculate result 
        .All(b => b) 
     ); 
+1

Để lấy mẫu bình luận một câu trả lời đã xóa (mà bạn không thể nhìn thấy): "Nhưng sau đó bạn cần phải phủ nhận kết quả từ Bất kỳ để có được kết quả tương tự như được lấy từ Tất cả (đúng nếu nó đúng cho tất cả, sai nếu không) và bạn quay lại ngay với tình huống tương tự " – DavidG

+0

@ Damavg damn, đúng vậy. – RoadieRich

+0

@DavidG là phiên bản mới tốt hơn? – RoadieRich

12

Nếu bạn đang sử dụng LINQ to Objects, tôi chỉ muốn viết phương pháp khuyến nông của riêng tôi. Edulinq project tôi có mẫu mã cho All, và thích nghi với điều đó là khá đơn giản:

public static bool AnyAndAll<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate) 
{ 
    if (source == null) 
    { 
     throw new ArgumentNullException(nameof(source)); 
    } 
    if (predicate == null) 
    { 
     throw new ArgumentNullException(nameof(predicate)); 
    } 

    bool any = false; 
    foreach (TSource item in source) 
    { 
     any = true; 
     if (!predicate(item)) 
     { 
      return false; 
     } 
    } 
    return any; 
} 

này tránh việc đánh giá đầu vào nhiều hơn một lần.

0

Dưới đây là một mẹo:

var exist = dbContext.Definitions 
    .Where(x => propertyTypeIds.Contains(x.PropertyTypeId) && x.CountryId == countryId) 
    .GroupBy(x => x.PropertyTypeId) 
    .Min(some_condition ? (int?)1 : 0) == 1; 

Nó sử dụng thực tế là lợi nhuận Min<int?> phương pháp trên:

(A) null khi tập rỗng
(B) 0 nếu điều kiện không hài lòng đối với một số thành phần
(C) 1 nếu điều kiện được đáp ứng cho tất cả các thành phần

vì vậy chúng tôi đơn giản kiểm tra kết quả cho (C) bằng cách sử dụng quy tắc so sánh giá trị có thể có giá trị.

Các vấn đề liên quan