2009-10-20 9 views
5

У меня есть метод, который в настоящее время принимает параметр Func<Product, string>, но мне нужно, чтобы он был Expression<Func<Product, string>>. Используя AdventureWorks, вот пример того, что я хотел бы сделать, используя Func.Рефакторинг Func <T> в выражение <Func<T>>

private static void DoSomethingWithFunc(Func<Product, string> myFunc) 
{ 
    using (AdventureWorksDataContext db = new AdventureWorksDataContext()) 
    { 
     var result = db.Products.GroupBy(product => new 
     { 
      SubCategoryName = myFunc(product), 
      ProductNumber = product.ProductNumber 
     }); 
    } 
} 

Я хотел бы, чтобы это выглядело примерно так:

private static void DoSomethingWithExpression(Expression<Func<Product, string>> myExpression) 
{ 
    using (AdventureWorksDataContext db = new AdventureWorksDataContext()) 
    { 
     var result = db.Products.GroupBy(product => new 
      { 
       SubCategoryName = myExpression(product), 
       ProductNumber = product.ProductNumber 
      }); 
    } 
} 

Однако проблема я бегу в том, что myExpression(product) является недействительным (не компилировать). После прочтения некоторых других сообщений я понимаю, почему. И если бы это был не тот факт, что мне нужно переменную product для второй части моего ключа я мог бы сказать что-то вроде этого:

var result = db.Products.GroupBy(myExpression); 

Но мне нужна переменная product, потому что мне нужно второе часть ключа (ProductNumber). Поэтому я не совсем уверен, что делать сейчас. Я не могу оставить его как Func, потому что это вызывает проблемы. Я не могу понять, как использовать выражение, потому что не вижу, как я могу передать ему переменную product. Есть идеи?

EDIT: Вот пример того, как я назвал бы метод:

DoSomethingWithFunc(product => product.ProductSubcategory.Name); 

ответ

4

Там нет никакого способа, чтобы склеить дерево выражения, которое представлена ​​как Expression<T> объекта в середину «дерева буквального», представленной лямбда выражение. Вы должны построить дерево выражения для передачи GroupBy вручную:

// Need an explicitly named type to reference in typeof() 
private class ResultType 
{ 
    public string SubcategoryName { get; set; } 
    public int ProductNumber { get; set; }| 
} 

private static void DoSomethingWithExpression(
    Expression<Func<Product, 
    string>> myExpression) 
{ 
    var productParam = Expression.Parameter(typeof(Product), "product"); 
    var groupExpr = (Expression<Func<Product, ResultType>>)Expression.Lambda(
     Expression.MemberInit(
      Expression.New(typeof(ResultType)), 
      Expression.Bind(
       typeof(ResultType).GetProperty("SubcategoryName"), 
       Expression.Invoke(myExpression, productParam)), 
      Expression.Bind(
       typeof(ResultType).GetProperty("ProductNumber"), 
       Expression.Property(productParam, "ProductNumber"))), 
     productParam); 
    using (AdventureWorksDataContext db = new AdventureWorksDataContext()) 
    { 
     var result = db.Products.GroupBy(groupExpr); 
    } 
} 
+0

Ницца! Последняя строка не компилируется для меня, хотя, где назначается результат. «Аргументы типа для метода ... не могут быть выведены из использования». Я что-то упускаю? – Ecyrb

+1

Возвращаемое значение из 'Expression.Lambda' должно быть выбрано в выражение >'. –

+0

Отлично! Это оказалось более сложным, чем я ожидал. Я никогда раньше не делал свое собственное выражение таким образом, поэтому я изучу этот код, чтобы убедиться, что полностью понимаю, что происходит. Благодаря! – Ecyrb

3

На второй мысли, компиляции выражение не будет работать.

Вам нужно будет создать свое выражение GroupBy вручную, что означает, что вы не можете использовать анонимный тип. Я бы предложил построить оставшуюся часть вашего выражения, а затем декомпилировать, чтобы увидеть сгенерированное дерево выражений. Конечный результат будет выглядеть примерно так, используя часть myExpression в зависимости от обстоятельств:

private static void DoSomethingWithExpression(Expression<Func<Product, string>> myExpression) 
{ 
    var productParam = myExpression.Parameters[0]; 

    ConstructorInfo constructor = ...; // Get c'tor for return type 

    var keySelector = Expression.Lambda(
          Expression.New(constructor, 
           new Expression[] { 
            productParam.Body, 
            ... // Expressions to init other members 
           }, 
           new MethodInfo[] { ... }), // Setters for your members 
          new [] { productParam }); 

    using (AdventureWorksDataContext db = new AdventureWorksDataContext()) 
    { 
     var result = db.Products.GroupBy(keySelector); 

     // ... 
    } 
} 
+0

Вы пропустили причину, почему он движется от '' Func' в Expression' в первую очередь - он хочет, чтобы работать в LINQ к SQL контекст, и это не позволит выполнять случайные функции (или делегировать) вызовы в запросе. –

+0

Да, я вижу это сейчас - работаю над Редактированием. – dahlbyk

+0

Вы избили Павла до удара, но его ответ был более ясен. +1 за вашу помощь. Благодаря! – Ecyrb