2012-01-25 10 views
6

Tôi đã hỏi very similar question ngày hôm qua, nhưng đến tận hôm nay tôi mới nhận ra câu trả lời tôi chấp nhận không giải quyết được tất cả các vấn đề của tôi. Tôi có đoạn mã sau:Làm cách nào để tạo một thuộc tính đa năng Chọn trên một số điện thoại có thể truy cập được là <T> khi chạy?

public Expression<Func<TItem, object>> SelectExpression<TItem>(string fieldName) 
{ 
    var param = Expression.Parameter(typeof(TItem), "item"); 
    var field = Expression.Property(param, fieldName); 
    return Expression.Lambda<Func<TItem, object>>(field, 
     new ParameterExpression[] { param }); 
} 

nào được sử dụng như sau:

string primaryKey = _map.GetPrimaryKeys(typeof(TOriginator)).Single(); 
var primaryKeyExpression = SelectExpression<TOriginator>(primaryKey); 
var primaryKeyResults = query.Select(primaryKeyExpression).ToList(); 

này cho phép tôi để kéo ra khóa chính từ một IQueryable<TUnknown>. Vấn đề là mã này chỉ hoạt động với một khóa chính và tôi cần thêm hỗ trợ cho nhiều PK. Vì vậy, có cách nào tôi có thể thích ứng với phương pháp SelectExpression ở trên để lấy một IEnumerable<string> (danh sách các tên thuộc tính khóa chính của tôi) và có phương thức trả lại một biểu thức chọn các khóa đó không? Không.

I.e. Với những điều sau:

var knownRuntimePrimaryKeys = new string[] { "CustomerId", "OrderId" }` 

My Chọn cần phải làm như sau (khi chạy):

var primaryKeys = query.Select(x=> new { x.CustomerId, x.OrderId }); 
+1

Vấn đề là loại vô danh mà là kết quả của statemet chọn được tạo ra tại thời gian biên dịch .. – m0sa

+1

Có một thay thế cho việc sử dụng một loại Chưa xác định và vẫn đạt được những gì tôi yêu cầu? Hay là nó quay trở lại bản vẽ? – GenericTypeTea

+0

@GenericTypeTea, Tuple có phải là tùy chọn có thể chấp nhận cho bạn không? –

Trả lời

2

Bạn có thể sử dụng Tuple<> vì kiểu nặc danh phải được biết tại thời gian biên dịch:

public Expression<Func<TItem, object>> SelectExpression<TItem>(params string[] fieldNames) 
{ 
    var param = Expression.Parameter(typeof(TItem), "item"); 
    var fields = fieldNames.Select(x => Expression.Property(param, x)).ToArray(); 
    var types = fields.Select(x => x.Type).ToArray(); 
    var type = Type.GetType("System.Tuple`" + fields.Count() + ", mscorlib", true); 
    var tuple = type.MakeGenericType(types); 
    var ctor = tuple.GetConstructor(types); 
    return Expression.Lambda<Func<TItem, object>>(
     Expression.New(ctor, fields), 
     param 
    ); 
} 

và sau đó:

var primaryKeyExpression = SelectExpression<TOriginator>("CustomerId", "OrderId"); 

sẽ tạo ra các biểu thức sau đây:

item => new Tuple<string, string>(item.CustomerId, item.OrderId) 
+1

Thật không may điều này dường như không hoạt động với IQueryable từ Entity Framework. "Chỉ có các hàm tạo và tham số không tham số được hỗ trợ trong LINQ to Entities." – GenericTypeTea

+0

@GenericTypeTea, oh, chà, thật là buồn. Trong trường hợp này, bạn có thể sử dụng Reflection.Emit để tạo ra một kiểu động khi chạy với số lượng thuộc tính thích hợp và sau đó xây dựng một biểu thức gán các thuộc tính đó. Có vẻ như rất nhiều công việc. –

+0

Tôi bắt đầu tự hỏi liệu tốt hơn là chỉ ép buộc ứng dụng tiêu thụ thực hiện một giao diện mà tôi chỉ định trên tất cả các đối tượng EF POCO của họ. Sau đó, tôi có thể yêu cầu họ triển khai phương thức GetPrimaryKeySelectExpression ... nhưng điều đó sẽ dẫn đến tất cả các vấn đề có thể xảy ra (không bao gồm thực tế nó phá vỡ toàn bộ nguyên tắc của một lớp POCO) cũng như là một nỗi đau hoàn toàn trong b ' để người dùng thực hiện. Đu và vòng xuyến! – GenericTypeTea

1

Như ai đó đã chỉ ra, bạn là chủ yếu cố gắng để có được một loại vô danh được xây dựng trong thời gian chạy, mà không phải là đi làm việc.

Có cách nào khác để sử dụng loại Ẩn danh và vẫn đạt được những gì tôi yêu cầu không?

Điều này thực sự phụ thuộc vào ý bạn. Câu trả lời rõ ràng là sử dụng cấu trúc thời gian chạy, chẳng hạn như từ điển, tuple, v.v. Nhưng bạn có thể nhận thức đầy đủ về điều này, vì vậy tôi giả sử rằng bạn muốn kết quả biên dịch theo thời gian với tên trường thực, để bất kỳ sử dụng sai số primaryKeys bị bắt trong quá trình biên dịch.

Nếu có, thì tôi sợ tùy chọn duy nhất của bạn là tạo mã có liên quan trước khi biên dịch. Đây không phải là xấu như nó có thể âm thanh, nhưng không phải là hoàn toàn minh bạch: khi bạn thay đổi giản đồ, bạn phải chạy lại việc tạo mã bằng cách nào đó.

Tại công ty chúng tôi, chúng tôi đã thực hiện chính xác điều đó, được lấy cảm hứng từ SubSonic nhưng thấy rằng SubSonic chính nó không hoàn toàn là những gì chúng tôi muốn. Nó làm việc khá tốt theo ý kiến ​​của tôi.

+0

Tôi không có kiến ​​thức về các khóa chính khi chạy. Đoạn mã trên được sử dụng bởi một ứng dụng tiêu thụ mà tôi không có quyền kiểm soát. Xem câu hỏi trước để có giải thích tốt hơn. – GenericTypeTea

3

Không có cách nào dễ dàng để thực hiện chính xác những gì bạn muốn, vì nó sẽ yêu cầu bạn tạo kiểu mới động (các kiểu ẩn danh được tạo bởi trình biên dịch khi chúng được biết một cách tĩnh). Trong khi nó chắc chắn là khả thi, nó có lẽ không phải là lựa chọn dễ dàng nhất ...

Bạn có thể đạt được một kết quả tương tự như sử dụng tuples:

public Expression<Func<TItem, object>> SelectExpression<TItem>(string[] propertyNames) 
{ 
    var properties = propertyNames.Select(name => typeof(TItem).GetProperty(name)).ToArray(); 
    var propertyTypes = properties.Select(p => p.PropertyType).ToArray(); 
    var tupleTypeDefinition = typeof(Tuple).Assembly.GetType("System.Tuple`" + properties.Length); 
    var tupleType = tupleTypeDefinition.MakeGenericType(propertyTypes); 
    var constructor = tupleType.GetConstructor(propertyTypes); 
    var param = Expression.Parameter(typeof(TItem), "item"); 
    var body = Expression.New(constructor, properties.Select(p => Expression.Property(param, p))); 
    var expr = Expression.Lambda<Func<TItem, object>>(body, param); 
    return expr; 
} 
+2

Thật không may điều này dường như không hoạt động với IQueryable từ Entity Framework. "Chỉ có các hàm tạo và tham số không tham số được hỗ trợ trong LINQ to Entities." – GenericTypeTea

+1

@GenericTypeTea Có lẽ nó là hiển nhiên, nhưng tôi đã giải quyết thông báo lỗi này với biên dịch biểu thức trước khi sử dụng, ví dụ: var primaryKeyResults = query.Select (primaryKeyExpression.Compile()). ToList(); –

+2

@ Jorr.it Nhận xét của bạn có thể đúng nhưng truy vấn sẽ không được thực hiện trên cơ sở dữ liệu – Tokk