5

我有一个当前将 aFunc<Product, string>作为参数的方法,但我需要它是一个Expression<Func<Product, string>>. 使用 AdventureWorks,这是我想使用 Func 做的一个示例。

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

2 回答 2

4

无法将表示为Expression<T>对象的表达式树拼接到由 lambda 表达式表示的“树文字”的中间。您必须构建一个表达式树以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);
    }
}
于 2009-10-20T17:21:49.217 回答
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);

        // ...
    }
}
于 2009-10-20T17:06:03.897 回答