2009-10-20 102 views
5

我有一个方法,目前需要一个Func<Product, string>作为参数,但我需要它是一个Expression<Func<Product, string>>。使用AdventureWorks,这里是我想用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变量。有任何想法吗?

编辑:这里是我如何调用该方法的例子:

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 2009-10-20 17:42:12

+1

应该将'Expression.Lambda'的返回值转换为'Expression >'。 – 2009-10-20 17:48:06

+0

优秀!事实证明,这比我期待的要复杂得多。我从来没有像之前那样创建过自己的表达式,所以我会研究这些代码以确保我完全理解正在发生的事情。谢谢! – Ecyrb 2009-10-20 17:55:11

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上下文,并且这将不允许查询中的随机函数(或委托)调用。 – 2009-10-20 17:09:04

+0

是的,我现在看到 - 正在编辑。 – dahlbyk 2009-10-20 17:11:47

+0

你击败了Pavel,但他的回答对我来说更加清楚。不过,请+1帮忙。谢谢! – Ecyrb 2009-10-20 17:56:28

相关问题