3

给定这样的类:

public class AnEntity
{
    public int prop1 { get; set; }
    public string prop2 { get; set; }
    public string prop3 { get; set; }
}

我能够生成一个选择一个属性的 lambda 表达式,如下所示:

ParameterExpression pe = Expression.Parameter(typeof(AnEntity), "x");
MemberExpression selectClause = Expression
    .MakeMemberExpression(
        pe, 
        typeof(AnEntity).GetProperty(prop2)); // selecting prop2

var selectLambda = Expression.Lambda<Func<AnEntity, object>>(selectClause, pe);

然后我可以像这样使用 lambda 表达式:

IQueryable<AnEntity> myEntities = dbContext.MyEntities.AsQueryable();
var results = myEntities.Select(selectLambda);

如何向 selectLambda 添加第二个选择子句?例如,如何同时选择 prop2 和 prop3?

4

3 回答 3

2

Below is a fleshed out example of what "usr" described in his solution using MemberInitExpression.

For my solution, I am going to provide you with a new class that you will want write the expression result into. In reality, this could be the POCO for the entity itself, but by specifying a different class, it is clearer which class is the class you are projecting into as compared to the class you are projecting from. As "usr" mentioned, you can also try to use Tuple or other constructs. My current favorite is to use the extra code I appended to the bottom to create a new type dynamically. This is a bit more flexible than Tuple, but it has some disadvantages in that it requires Reflection to access it for the most part.

Class to project into:

public class Holder
{
    public int Item1{ get; set; }
    public string Item2 { get; set; }
    public string Item3 { get; set; }
}

Code to create expression:

ParameterExpression paramExp = Expression.Parameter(typeof(AnEntity));
NewExpression newHolder = Expression.New(typeof(Holder));    
Type anonType = typeof(Holder);                
MemberInfo item1Member = anonType.GetMember("Item1")[0];
MemberInfo item2Member = anonType.GetMember("Item2")[0];
MemberInfo item3Member = anonType.GetMember("Item3")[0];

// Create a MemberBinding object for each member 
// that you want to initialize.
 MemberBinding item1MemberBinding =
 Expression.Bind(
     item1Member,
     Expression.PropertyOrField(paramExp, "prop1"));
 MemberBinding item2MemberBinding =
 Expression.Bind(
     item2Member,
     Expression.PropertyOrField(paramExp, "prop2"));
 MemberBinding item3MemberBinding =
 Expression.Bind(
     item3Member,
     Expression.PropertyOrField(paramExp, "prop3"));

// Create a MemberInitExpression that represents initializing 
// two members of the 'Animal' class.
MemberInitExpression memberInitExpression =
    Expression.MemberInit(
        newHolder,
        item1MemberBinding,
        item2MemberBinding,
        item3MemberBinding);

var lambda = Expression.Lambda<Func<AnEntity, Holder>>(memberInitExpression, paramExp);

Finally, how you would call the expression:

IQueryable<AnEntity> myEntities = dbContext.MyEntities.AsQueryable();
var results = myEntities.Select(selectLambda);

Here is some additional code if you wanted to define a type dynamically for the return value:

    public static Type CreateNewType(string assemblyName, string typeName, params Type[] types)
    {
        // Let's start by creating a new assembly
        AssemblyName dynamicAssemblyName = new AssemblyName(assemblyName);
        AssemblyBuilder dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, AssemblyBuilderAccess.Run);
        ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule(assemblyName);

        // Now let's build a new type
        TypeBuilder dynamicAnonymousType = dynamicModule.DefineType(typeName, TypeAttributes.Public);

        // Let's add some fields to the type.
        int itemNo = 1;
        foreach (Type type in types)
        {
            dynamicAnonymousType.DefineField("Item" + itemNo++, type, FieldAttributes.Public);
        }

        // Return the type to the caller
        return dynamicAnonymousType.CreateType();
    }
于 2014-05-30T05:27:01.087 回答
1

回答此类问题的方法是在强类型 C# ( ) 中编写您想要构建的模式,select new { x.p1, x.p2 }并使用调试器查看表达式树。然后你自己建造那棵树。

您会发现一个MemberInitExpression具有两个属性的类的实例化,p1并且p2正在从x.

您必须以某种方式提供这样的课程。要么自己定义(class X { string p1, p2; }),要么使用Tuple-like 构造。或者一个object[].

了解您只能有一个返回值。所以它需要封装多个值。

最简单的方法是使用object[]. 看看 C# 编译器是如何做到的。

于 2013-07-11T22:13:40.237 回答
0

没有“第二个选择返回值”。您可以返回一个值,它可能是一个聚合值。

C# 语言在这方面提供了很多语法糖,以 LINQ 和匿名类型的形式。要通过由表达式树构建的 lambda 返回聚合,您必须创建一个类型来保存所有不同的值(就像 C# 编译器在看到匿名类型时在幕后所做的那样),然后调用它的构造函数传递您想要返回的多个值(这实际上比 C# 在后台执行的操作要容易一些,即调用一堆属性设置器)。在运行时,没有匿名类型。它们都被命名,如果不是由程序员命名,那么由编译器命名。

实际上,您可能可以使用 aTuple<Type1, Type2, Type3>而不是专门为此目的创建一个类。但是,您的成员属性将不会很好地命名。

于 2013-07-11T22:14:14.783 回答