2013-06-14 2 views
2

Редактировать Я думаю, что могу попросить лучше (в этом случае вообще не нужен код). Итак, вопрос в целом заключается в следующем: как использовать дерево выражений для построения вызова общего метода (Select<TEntity,TResult> в моем случае), когда TResult создан во время выполнения? Игнорируйте весь код и текст ниже, это была непонятная версия вопроса, оставив его не путать тех, кто ответил.Дерево выражений для перечислимого. Выберите

Мне нужен пример построения дерева выражений для вызова «Выбрать». Проблема в том, что я не знаю тип результата во время компиляции (он может быть определен пользователем через некоторый графический интерфейс во время выполнения). Вот код, как я пытаюсь сделать это (помните, что я не могу использовать дженерики)

class Pet { 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

Используя этот класс в Main:

List<Pet> list = new List<Pet>();     
Expression eList = Expression.Constant(list); 
ParameterExpression pe = Expression.Parameter(typeof(Pet), "p"); 
MethodInfo method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic); 
var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user 
Expression selectorBody = Expression.Call(method, Expression.Constant(properties)); 
Expression selector = Expression.Lambda(selectorBody, pe); 
Expression res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector); 

То, что я пытаюсь сделать создает тип во время выполнения с использованием Reflection.Emit (метод «CreateType» в коде выше, он используется в некоторых моих проектах и ​​отлично выглядит) и каким-то образом передает его для вызова «Выбрать», но я получаю исключение:

Нет универсального метода «Выбрать» по типу «System.Linq.Enumerab le 'совместим с аргументами и аргументами поставляемого типа.

Любые предложения?

Обновление моя настоящая проблема заключается в создании дерева выражений для вызова «Join» для типов времени выполнения, что является более сложным, поэтому я решил спросить о «Select», потому что у меня такое же исключение в последнем line (Expression.Call (...))

Upd2 включил мои вспомогательные методы и отредактировал основной код, однако это не основная проблема.

static Type CreateType(IEnumerable<PropertyInfo> properties) { 
     TypeBuilder typeBuilder = CreateTypeBuilder("ResultDynamicAssembly", "ResultModule", "ResultType"); 
     foreach (PropertyInfo propertyInfo in properties) { 
      CreateAutoImplementedProperty(typeBuilder, propertyInfo.Name, propertyInfo.PropertyType); 
     } 
     return typeBuilder.CreateType(); 
    } 

    static object CreateObject(IEnumerable<PropertyInfo> properties) { 
     Type type = CreateType(properties); 
     return Activator.CreateInstance(type); 
    } 
+0

Можете ли вы опубликовать свой код? Если дерево выражений, которое вы строите, является сложным, почти невозможно спрогнозировать, что может быть неправильным. –

ответ

4

Вы можете упростить эту проблему, удалив динамический тип из уравнения. Вы можете воспроизвести ту же проблему с приведенным ниже кодом, который делает то же самое, но без динамического типа.

static class Program 
{ 
    private static void Main(string[] args) 
    { 
     var list = new List<Pet>(); 
     var eList = Expression.Constant(list); 
     var pe = Expression.Parameter(typeof(Pet), "p"); 
     var method = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic); 
     var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user 
     var selectorBody = Expression.Call(method, Expression.Constant(properties)); 
     var selector = Expression.Lambda(selectorBody, pe); 
     var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), CreateType(properties) }, eList, selector); 
    } 

    private static Type CreateType(IEnumerable<PropertyInfo> properties) 
    { 
     return typeof (DynamicType); 
    } 

    private static object CreateObject(IEnumerable<PropertyInfo> properties) 
    { 
     var type = CreateType(properties); 
     return Activator.CreateInstance(type); 
    } 

    class Pet 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
    } 

    class DynamicType 
    { 
     public string Name { get; set; } 
    } 
} 

Таким образом, проблема заключается в подписи метода CreateObject. Поскольку его тип возврата не является динамическим типом, лямбда недействительна. Вы можете увидеть это, изменив тип CreateObject.

// this works fine 
    private static DynamicType CreateObject(IEnumerable<PropertyInfo> properties) 
    { 
     var type = CreateType(properties); 
     return (DynamicType) Activator.CreateInstance(type); 
    } 

Так как вы имеете дело с динамическим типом, вам нужно построить выражение для приведения результата CreateObject к вашему динамическому типу. Попробуйте использовать что-то вроде этого:

 // create dynamic type 
     var properties = typeof(Pet).GetProperties().Where(pi => pi.Name == "Name"); //will be defined by user 
     var dynamicType = CreateType(properties); 

     // build expression 
     var list = new List<Pet>(); 
     var eList = Expression.Constant(list); 
     var pe = Expression.Parameter(typeof(Pet), "p"); 
     var createObjectMethod = typeof(Program).GetMethod("CreateObject", BindingFlags.Static | BindingFlags.NonPublic); 
     var createObjectCall = Expression.Call(createObjectMethod, Expression.Constant(properties)); 
     var castExpression = Expression.Convert(createObjectCall, dynamicType); 
     var selectorExpression = Expression.Lambda(castExpression, pe); 
     var res = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Pet), dynamicType }, eList, selectorExpression); 
+0

Спасибо за ваш ответ, однако DynamicType из вашего предложения не может быть явно объявлен в моем случае. Рассмотрим следующий сценарий: класс Customer со свойствами Name, Phone, Country, Id, пользователь может выбрать любой из них для включения в новый тип (например, с помощью флажков), и моя цель - создать этот тип во время выполнения и использовать его в вызовах к общим методам –

+0

Я понимаю. Приведенный выше код просто демонстрирует, что проблема не вытекает непосредственно из вашего использования динамического типа. Добавьте вызов Expression.Convert(), как показано выше, и ваша проблема будет исправлена. Если это не так, нам все еще нужно больше деталей. –

+0

О, теперь я вижу, спасибо, что исправил это –

-1

Создайте завод для каждого типа. Вы знаете, что источник, так это очень просто:

interface PetResultFactory<T> 
{ 
    string TypeName {get; } 
    List<T> Transform(IEnumerable<Pet> pets); 
} 

Вы должны зарегистрировать все возможные типы в контейнере и показать зарегистрированных типов на пользовательском интерфейсе для пользователей. Когда пользователь выбирает один, вы знаете, как создать этот тип, потому что он описан на заводе.

+0

Это не отвечает на вопрос. Вопрос конкретно связан с построением дерева выражений. Неизвестный тип на самом деле не является существенной частью вопроса. –

Смежные вопросы