2009-12-30 45 views
10

Tôi có điều này LINQ truy vấn:Tại sao LINQ không kết hợp câu lệnh này?

// types... 
    LinkedList<WeightedItem> itemScores = new LinkedList<WeightedItem>(); 

    var result = from i in _ctx.Items 
       join s in itemScores on i.Id equals s._id 
       orderby s._score descending 
       select new ItemSearchResult(i, s._score); 

    // this fails: 
    return result.ToList(); 

Đó là tạo ra lỗi này:

Unable to create a constant value of type 'System.Collections.Generic.IEnumerable`1'.
Only primitive types ('such as Int32, String, and Guid') are supported in this context.

[EDIT] Dưới đây là đoạn code của WeightedItem:

public class WeightedItem 
{ 
    public int _id; 
    public decimal? _score; 

    public WeightedItem(int id, decimal? score) 
    { 
     _id = id; 
     _score = score; 
    } 
} 

Bạn có thể nhìn thấy những gì tôi đã làm sai? Mã biên dịch hoàn hảo và cả _ctx.Items và itemScores chứa các giá trị thích hợp.

+0

Bạn có thể đăng mã cho WeightedItem – Lazarus

+0

Rõ ràng WeightedItem không phải là một kiểu nguyên thủy. – DOK

+0

Lazarus, xong rồi. DOK, có nghĩa là gì? – Mickel

Trả lời

21

Có, nó sẽ biên dịch tốt - vấn đề là nó không thể dịch nó thành SQL. Khi bạn tham chiếu các giá trị "cục bộ", khung thực thể phải tìm ra những gì cần làm với chúng khi cần tạo một truy vấn SQL. Về cơ bản nó không thể đối phó với việc tham gia giữa một bộ sưu tập trong bộ nhớ và một bảng cơ sở dữ liệu.

Một điều mà có thể hoạt động sẽ sử dụng Contains thay thế. Tôi không biết liệu LinkedList<T> sẽ làm việc cho điều này, nhưng tôi tin rằng List<T> có, ít nhất là trong LINQ to SQL:

List<int> requiredScoreIds = itemScores.Select(x => x._id).ToList(); 

var tmp = (from i in _ctx.Items 
      where requiredScoreIds.Contains(i.Id) 
      orderby s._score descending 
      select i).AsEnumerable(); 

// Now do the join in memory to get the score 
var result = from i in tmp 
      join s in itemScores on i.Id equals s._id 
      select new ItemSearchResult(i, s._score); 

Bây giờ đó là làm một tham gia truy vấn trong bộ nhớ, trong đó có phần không cần thiết. Thay vào đó, bạn có thể sử dụng từ điển:

List<int> requiredScoreIds = itemScores.Select(x => x._id).ToList(); 

var tmp = (from i in _ctx.Items 
      where requiredScoreIds.Contains(i.Id) 
      orderby s._score descending 
      select i).AsEnumerable(); 

// Create a map from score ID to actual score 
Dictionary<int, decimal?> map = itemScores.ToDictionary(x => x._id, 
                 x => x._score); 

var result = tmp.Select(i => new ItemSearchResult(i, map[i.Id])); 
+0

có ý nghĩa, vì vậy .AsEnumerable() thực hiện truy vấn và lưu kết quả trong bộ nhớ? Nếu không, phần nào của mã sẽ làm gì? – Mickel

+2

@Mickel: 'AsEnumerable' không thực hiện truy vấn ngay lập tức - nhưng nó trả về một' IEnumerable 'thay vì' IQueryable ', vì vậy phần còn lại của truy vấn sẽ được thực hiện bằng' Enumerable.xxx' thay vì 'Queryable. xxx'. Khi truy vấn đó cuối cùng cần thực hiện, nó sẽ thực hiện phần đầu tiên trong cơ sở dữ liệu và phần thứ hai trong bộ nhớ. –

3

Bạn không thể kết hợp giữa danh sách bộ nhớ và đối tượng có điều kiện. Bạn cần phải làm một cái gì đó như thế này:

var criteria = itemScores.Select(x => x._id).ToList(); 
var result_tag = (from i in _ctx.Items 
       where criteria.Contains(i.ID) 
       select i).ToList(); 
var result = from i in result_tag 
      join s in itemScores on i.ID equals s._id 
      orderby s._score descending 
      select new ItemSearchResult(i, s._score); 
+5

Ah shucks - Jon Skeet đánh tôi :) –

+1

Ông luôn thắng ... – Ragepotato

+0

Jon Skeet là Chuck Norris của StackOverflow –

1

Chỉ trong trường hợp bảng đại diện bởi _ctx.Items không phải là một vấn đề lớn và bạn không quan tâm đến tải tất cả các bảng trong bộ nhớ và sau đó lọc nó trong bộ nhớ , bạn chỉ có thể hoán đổi thứ tự của các mục trong sự tham gia tuyên bố, như trong đoạn mã sau:

LinkedList<WeightedItem> itemScores = new LinkedList<WeightedItem>(); 

var result = from s in itemScores 
      join i in _ctx.Items on s._id equals i.Id 
      orderby s._score descending 
      select new ItemSearchResult(i, s._score); 

return result.ToList(); 

trong báo cáo kết quả ban đầu phương pháp khuyến nông queryable được gọi:

IQueryable<TResult> Queryable.Join<TOuter, TInner, TKey, TResult>(
     this IQueryable<TOuter> outer, 
     IEnumerable<TInner> inner, 
     Expression<Func<TOuter, TKey>> outerKeySelector, 
     Expression<Func<TInner, TKey>> innerKeySelector, 
     Expression<Func<TOuter, TInner, TResult>> resultSelector 
) 

trong khi trong một trao đổi các phương pháp khuyến nông Enumerable được gọi:

IEnumerable<TResult> Enumerable.Join<TOuter, TInner, TKey, TResult>(
     this IEnumerable<TOuter> outer, 
     IEnumerable<TInner> inner, 
     Func<TOuter, TKey> outerKeySelector, 
     Func<TInner, TKey> innerKeySelector, 
     Func<TOuter, TInner, TResult> resultSelector 
) 

như vậy trong báo cáo kết quả cuối cùng bảng _ctx.Items đầy đủ được nạp vào bộ nhớ và sau đó tham gia, qua LINQ to Objects, vào danh sách itemScores (Tôi không biết về LinkedList, tôi đã thử nó với Danh sách).

Tôi đã thêm câu trả lời này chủ yếu bởi vì ai đó có thể nhập tham gia theo thứ tự ngược lại và để công việc này thậm chí không nhận ra điều gì sẽ xảy ra trong cơ sở dữ liệu.

Tôi sẽ không đề xuất tham gia theo cách này, mặc dù nó có thể hữu ích cho các ứng dụng backoffice bất cứ khi nào các bảng có liên quan được tạo thành từ ít hồ sơ và ứng dụng không bị ảnh hưởng xấu. Giải pháp này, sau khi tất cả, giữ sạch mã.

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