3

次のようなクラスがあるとします。

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

次のような 1 つのプロパティを選択するラムダ式を生成できます。

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);

次に、次のようにラムダ式を使用できます。

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

selectLambda に 2 番目の select 句を追加するにはどうすればよいですか? たとえば、prop2 と prop3 の両方を選択するにはどうすればよいでしょうか?

4

3 に答える 3

2

以下は、MemberInitExpression を使用した彼のソリューションで説明されている "usr" の肉付けされた例です。

私の解決策として、式の結果を書き込む新しいクラスを提供します。実際には、これはエンティティ自体の POCO である可能性がありますが、別のクラスを指定することで、投影元のクラスと比較して、投影先のクラスがどのクラスであるかが明確になります。「usr」が述べたように、Tuple やその他の構造を使用することもできます。私の現在のお気に入りは、最後に追加した追加のコードを使用して新しい型を動的に作成することです。これは Tuple よりも少し柔軟性がありますが、ほとんどの部分でリフレクションがアクセスする必要があるという点で、いくつかの欠点があります。

射影するクラス:

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

式を作成するコード:

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);

最後に、式をどのように呼び出すか:

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

戻り値の型を動的に定義する場合の追加コードを次に示します。

    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 }) で記述し、デバッガーを使用して式ツリーを調べることです。次に、そのツリーを自分で作成します。

2 つのプロパティをMemberInitExpression持つクラスをインスタンス化し、から初期化されていることがわかります。p1p2x

何らかの形でそのようなクラスを提供する必要があります。自分で定義するか ( class X { string p1, p2; })、- Tuplelike 構造を使用してください。またはobject[]

戻り値は 1 つしかないことを理解してください。したがって、複数の値をカプセル化する必要があります。

最も簡単な方法は、object[]. C# コンパイラがそれを行う方法を見てください。

于 2013-07-11T22:13:40.237 に答える
0

「2番目の選択の戻り値」はありません。集計である可能性がある 1 つの値を返すことができます。

C# 言語は、LINQ と匿名型の形式で、この領域に多くの構文糖衣を提供します。式ツリーによって構築されたラムダを介して集計を返すには、すべての異なる値を保持する型を作成する必要があります (匿名型を認識したときに C# コンパイラが舞台裏で行うのと同じように)。複数の値を返したい場合 (これは実際には、C# が舞台裏で行うことよりもいくらか簡単です。これは一連のプロパティ セッターと呼ばれます)。実行時には、匿名型はありません。それらはすべて、プログラマーではなくてもコンパイラーによって名前が付けられます。

Tuple<Type1, Type2, Type3>実際、特に目的のためにクラスを作成する代わりに、おそらく a を使用できます。しかし、メンバー プロパティの名前は適切ではありません。

于 2013-07-11T22:14:14.783 に答える