2013-08-20 60 views
5

Khi làm việc với IQuerayble<TItem> chúng ta có thể gọi Select như thế này:Làm thế nào để khởi tạo và khởi tạo một đối tượng động trong cây biểu thức?

query.Select(item => new { A=item.Prop1, B=item.Prop2}); 

Select phương pháp dự đoán Expression<Func<TItem,TResult>>

tôi cần phải sử dụng ExpandoObject thay vì lớp vô danh nhưng tĩnh gõ.

Nếu có thể nó sẽ như thế nào:

query.Select(item => dynamic new ExpandoBoject { A=item.Prop1, B=item.Prop2}); 

Vì vậy, tôi muốn xây dựng biểu cây Expression<Func<TItem,ExpandoObject>> nơi thuộc tính của đối tượng được khởi tạo theo cách tương tự như với loại vô danh.
Chức năng động chỉ cần thiết để khởi tạo, do đó, ok rằng Func trả về ExpandoObject thay vì dynamic.

Tôi không thể tìm thấy nhiều tài liệu về Expression.Dynamic và các chất kết dính tương ứng mà tôi nên sử dụng.


Cập nhật 1

Tại sao tôi cần tất cả những thứ này?
Vì tôi muốn get primary keys.
Tôi muốn làm điều đó cho bất kỳ loại thực thể nào.

Tôi biết cách lấy danh sách các thuộc tính soạn PK, nhưng bây giờ tôi cần thực hiện một phép chiếu phức tạp của thực thể đến EntityKey. Vâng, có thể tương đương với đẳng cấp của lớp này.

var keys = context.Set<TEntity>().Where(Expression<Func<TEntity,bool>).Select(Expression<Func<TEntity,EntityKey>>); 

Như tôi đã lưu ý trong các chú thích lambdas chứa khối không thể chuyển đổi thành cây biểu thức để tôi không thể tạo từ điển đơn giản và điền vào. Bây giờ tôi đang chơi với cây biểu thức ngữ nghĩa gần với mã này:

var dict = new Dictionary<string,object>(); 
dict.Add("Prop1",value1); 
dict.Add("Prop2",value2); 
return dict 

Nhưng tôi nghi ngờ EF có thể phân tích biểu thức có chứa khối. Cần kiểm tra.
Và tôi tò mò liệu nó sẽ làm việc với các đối tượng động và Expression.MemberInit vì nó hoạt động với các đối tượng tĩnh.


Cập nhật 2

Entity Framework không hỗ trợ từ điển cú pháp khởi tạo.
Nó ném NotSupportedException với thông báo: Chỉ mục danh sách khởi tạo danh sách với một phần tử duy nhất được hỗ trợ trong LINQ to Entities.


Cập nhật 3

EF không hỗ trợ biểu thức khối là tốt.
NotSupportedException với thông báo: Biểu thức LINQ không xác định thuộc loại 'Chặn'.

+0

Bạn không thể làm điều gì đó tương tự như sau: 'query.Select (item => dynamic new ExpandoBoject {A = item.Prop1, B = item.Prop2});' Lippert đã nói với nó: http: // stackoverflow. com/questions/7478048/why-cant-i-do-này-động-x-new-expandoobject-foo-12-bar-mười hai. – xanatos

+0

Tôi biết, tôi không thể. Đó là một ví dụ về ý định xây dựng cây Expression với động lực. –

+0

Điều có thể được thực hiện là tạo một loại ẩn danh trong LINQ và sau đó, sau khi IQueryable đã hoàn tất, trong phần IEnumerable, hãy sao chép kiểu ẩn danh thành đối tượng Expando. Rõ ràng cả hai biểu thức có thể được tự động phát hiện theo một cách nào đó. – xanatos

Trả lời

3

Bây giờ tôi đang chơi với cây biểu ngữ nghĩa gần mã này:

var dict = new Dictionary<string,object>(); 
dict.Add("Prop1",value1); 
dict.Add("Prop2",value2); 
return dict; 

Bạn thể làm điều đó, bởi vì bạn có thể viết code mà như là một biểu hiện duy nhất như này:

new Dictionary<string, object> 
{ 
    { "Prop1", value1 }, 
    { "Prop2", value2 } 
}; 

Và bạn có thể tạo cây biểu thức có chứa biểu thức này (trong đó EF s có thể xử lý) như thế này:

var addMethod = typeof(Dictionary<string, object>).GetMethod("Add"); 

var expression = Expression.Lambda<Func<Dictionary<string, object>>>(
    Expression.ListInit(
     Expression.New(typeof(Dictionary<string, object>)), 
     Expression.ElementInit(
      addMethod, 
      Expression.Constant("Prop1"), 
      value1Expression), 
     Expression.ElementInit(
      addMethod, 
      Expression.Constant("Prop2"), 
      value2Expression)), 
    itemParameterExpression); 
+0

Tôi sẽ thử hôm nay, nhưng EF phàn nàn về cú pháp khởi tạo từ điển nói rằng nó chỉ hiểu các danh sách đơn giản. –

+0

Không, EF ném ngoại lệ. –

+0

@voroninp Trong trường hợp đó, bạn có thể thử một cái gì đó như 'new [] {Tuple.Create (" Prop1 ", value1), Tuple.Create (" Prop2 ", value2)}'. – svick

1

Điều được mô tả khó khăn chủ yếu là vì chúng tôi không thể tạo kiểu ẩn danh động trong thời gian chạy - chúng cần được biết lúc biên dịch. Vì vậy, đề xuất của tôi là tạo một lớp có thể chứa một số thuộc tính của kiểu được chọn tùy ý (tương tự như Tuple), tuy nhiên chúng tôi sẽ chỉ tải từ các giá trị db cho các thuộc tính quan trọng đối với chúng tôi. Vì vậy, chúng tôi cần lớp học như thế này:

public class CustomTuple<T1, T2> 
{ 
    public T1 Item1 { get; set; } 
    public T2 Item2 { get; set; } 
} 

Chúng tôi có thể thêm nhiều thuộc tính nếu cần thêm. Nếu chúng ta có lớp như vậy với 5 thuộc tính với việc sử dụng nó, chúng ta có thể tải tối đa 5 thuộc tính. Bây giờ logic chiếu:

Type[] parameterTypes = new Type[] { typeof(int), typeof(object) }; 
Type tupleType = typeof(CustomTuple<,>).MakeGenericType(parameterTypes); 
ParameterExpression x = Expression.Parameter(typeof(Entity)); 
NewExpression body = Expression.New(tupleType.GetConstructor(new Type[0]), new Expression[0]); 
MemberBinding binding1 = Expression.Bind(
    typeof(CustomTuple<,>).MakeGenericType(parameterTypes).GetProperty("Item1"), 
    Expression.Property(x, "Value")); 
MemberInitExpression memberInitExpression = 
    Expression.MemberInit(
     body, 
     binding1); 

Expression<Func<Entity, object>> exp = Expression.Lambda<Func<Entity, object>>(memberInitExpression, x); 
using (MyDbContext context = new MyDbContext()) 
{ 
    var list = context.Entities.Select(exp).ToList(); 
} 

Mã trên chọn từ giá trị Thu thập thực thể của thuộc tính Giá trị thuộc tính. parameterTypes định nghĩa các kiểu tham số được trả về từ Select projection. Nếu chúng ta không có ý định sử dụng thuộc tính đã cho thì chúng ta để nó như là kiểu đối tượng. Sau đó, chúng ta cần xây dựng biểu thức khởi tạo. Chúng ta làm điều này với phương thức New, chúng ta gán các giá trị thuộc tính với biểu thức được tạo bởi Expression.Bind và chúng ta kết hợp chúng với nhau bằng Expression.MemberInit. Chúng ta có thể tạo động trong thời gian chạy càng nhiều biểu thức MemberBinding như chúng ta cần.

+0

Thực ra, điều đó tương tự như những gì tôi đã làm. Nhưng tôi bắt đầu từ số không. Tạo tương tự của ExpandoObject với thừa kế được cho phép và sau đó tự động với CodeEmit tạo ra lớp tổ tiên với các thuộc tính bắt buộc. Điều đó cho phép tôi truy vấn chung về PK của các thực thể từ EF. –

+0

Ý tưởng tốt để tạo các lớp đó theo kiểu động. Tôi sẽ cố gắng chỉnh sửa câu trả lời của tôi để bao gồm điều này. – mr100

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