2013-09-27 46 views
6

Tôi đang cố gắng viết một LINQ thành phương thức mở rộng thực thể lấy Func để chọn một Id thuộc tính và so sánh nó với một danh sách các id.Làm thế nào để sử dụng một Func trong một biểu thức với LINQ to Entity Framework?

Lớp

public class A 
{ 
    public int AId { get; set; } 
} 

public class B 
{ 
    public int BId { get; set; } 
} 

Extension Method

public static IQueryable<T> WithId<T>(this IQueryable<T> entities, 
    Func<T, int> selector, IList<int> ids) 
    { 
     Expression<Func<T, bool>> expression = x => ids.Contains(selector(x)); 
     return entities.Where(expression); // error here (when evaluated) 
    } 

Calling Phương pháp

var ids = new List<int> { 1, 2, 3 }; 
DbContext.EntityAs.WithId(e => e.AId, ids); 
DbContext.EntityBs.WithId(e => e.BId, ids); 

Vấn đề tôi trải nghiệm là nó đang cố gắng gọi hàm không được phép trong khung thực thể.

Làm cách nào để sử dụng công cụ chọn thuộc tính (Func) để đánh giá truy vấn?

+0

Phạm vi mã bạn có thể gọi trong truy vấn EF bị giới hạn bởi thực tế nó vẫn cần được dịch trong SQL. Trong trường hợp của bạn, EF không biết cách tự động dịch một IList. –

+0

Tôi không chắc bạn có đúng với điều đó không. DbContext.EntityAs.Where (e => ids.Contains (e.Id)) được dịch bởi EF một cách chính xác. Tôi chỉ đang cố gắng tạo một hàm có thể sử dụng lại để tôi có thể xác định thuộc tính nào cần chọn. – David

+0

Vì EF biết cách thực hiện 'select x trong đó x in (1,2,3)' trong trường hợp liệt kê hoặc 'select x trong đó x in (select y)' trong trường hợp của một quan hệ thực thể khác. Trong trường hợp của bạn, EF sẽ cần phải biên dịch một cái gì đó như 'select x trong đó x in (chọn y trong đó F (y) trong (F (1), F (2), ...))'. Trong khi nó có thể làm điều này bằng tay EF chỉ không hỗ trợ các trường hợp * chưa * –

Trả lời

15

Bạn sẽ phải chuyển một số Expression<Func<T, int>> thay vì một số Func<T, int> và tự xây dựng biểu thức hoàn chỉnh. Điều này sẽ thực hiện thủ thuật:

public static IQueryable<T> WithId<T>(this IQueryable<T> entities, 
    Expression<Func<T, int>> propertySelector, ICollection<int> ids) 
{ 
    var property = 
     (PropertyInfo)((MemberExpression)propertySelector.Body).Member; 

    ParameterExpression parameter = Expression.Parameter(typeof(T)); 

    var expression = Expression.Lambda<Func<T, bool>>(
     Expression.Call(
      Expression.Constant(ids), 
      typeof(ICollection<int>).GetMethod("Contains"), 
      Expression.Property(parameter, property)), 
     parameter); 

    return entities.Where(expression); 
} 

Khi bạn cố gắng giữ mã của mình KHI LÀM khi làm việc với O/RM, bạn sẽ thường phải làm bằng cây biểu cảm. Đây là another fun example.

+0

Tuyệt vời. Tôi đã thử nghiệm cách xây dựng cây biểu thức từ http://blogs.msdn.com/b/miah/archive/2009/02/06/dynamic-expression-trees.aspx và http://stackoverflow.com/questions/ 820896/listobject-contains-expression-tree nhưng không thể tìm ra cách xây dựng Bộ sưu tập/Danh sách chứa. Cảm ơn bạn! – David

+3

@DavidLiddle: Tôi sẽ để cho bạn ở trên một bí mật nhỏ: Tôi chỉ cần viết truy vấn LINQ, biên dịch và mở Reflector để xem những gì trình biên dịch C# tạo ra. Bạn cũng có thể xem thông tin này trong trình gỡ lỗi, nhưng Reflector dễ dàng hơn nhiều. – Steven

+0

Bạn có thể đưa ra một ví dụ về "chỉ cần viết truy vấn LINQ". Sử dụng ILSpy tôi chỉ thấy chính xác truy vấn LINQ tôi đã viết! – David

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