2017-08-17 13 views
0

Linqクエリのselect文を動的に作成しようとしています。 私はこのような機能があります。ここでは複数の式を組み合わせてLinqセレクタ式を作成する

public Task<List<T>> RunQuery<T>(
    IQueryable<T> query, 
    FieldSelector<T> fields, 
    int pageNumber, int pageSize) 
{ 
    var skip = (pageNumber-1) * pageSize; 
    var query = query.Skip(skip).Take(pageSize); 
    var selector = fields.GetSelector(); 
    return query.Select(selector).ToListAsync(); 
} 

はFieldSelectorクラスである:(I私のコードは、私は、フィールドごとに余分な性質を持っている)GetSelector機能を実装する方法を

public class FieldSelector<T> 
{ 
    private List<LambdaExpression> expressions; 

    public FieldSelector() 
    { 
     expressions = new List<LambdaExpression>(); 
    } 

    public void Add(Expression<Func<T, object>> expr) 
    { 
     expressions.Add(expr); 
    } 

    public Expression<Func<T, object>> GetSelector() 
    { 
     //Build an expression like e => new {e.Name, e.Street} 
    } 
} 

?出来ますか? (複雑すぎることなく)。

var fields = new FieldSelector<Employee>(); 
fields.Add(e => e.Name); 
fields.Add(e => e.Street); 
RunQuery<Employee>(query, fields, 1, 100); 

答えて

3

あなたがダイナミックプロパティを持つ本当にAnonymous Typeカウントを生成することはできませんので、あなたは、それがAnonymous Typeのためのコンパイラをやっている方法をカスタムタイプを生成する必要があります。
これは私がそれを使用したい方法です。このタイプを生成したら、渡された式の割り当てを簡単に設定してFieldSelectorにして、それをカスタムタイプに組み合わせることができます。あなたは以上の1つのレベルのネストclass1->class2->class3->Propery_1でプロパティを取得しようとすると、ところで

public class FieldSelector<T> 
    { 
     private List<LambdaExpression> expressions; 

     public FieldSelector() 
     { 
      expressions = new List<LambdaExpression>(); 
     } 

     public void Add(Expression<Func<T, object>> expr) 
     { 
      expressions.Add(expr); 
     } 

     public Expression<Func<T, object>> GetSelector() 
     { 
      // We will create a new type in runtime that looks like a AnonymousType 
      var str = $"<>f__AnonymousType0`{expressions.Count}"; 

      // Create type builder 
      var assemblyName = Assembly.GetExecutingAssembly().GetName(); 
      var modelBuilder = AppDomain.CurrentDomain 
             .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect) 
             .DefineDynamicModule("module"); 
      var typeBuilder = modelBuilder.DefineType(str, TypeAttributes.Public | TypeAttributes.Class); 

      var types = new Type[expressions.Count]; 
      var names = new List<string>[expressions.Count]; 

      for (int i = 0; i < expressions.Count; i++) 
      { 
       // Retrive passed properties 
       var unExpr = expressions[i].Body as UnaryExpression; 
       var exp = unExpr == null ? expressions[i].Body as MemberExpression : unExpr.Operand as MemberExpression; 
       types[i] = exp.Type; 
       // Retrive a nested properties 
       names[i] = GetAllNestedMembersName(exp); 
      } 

      // Defined generic parameters for custom type 
      var genericParams = typeBuilder.DefineGenericParameters(types.Select((_, i) => $"PropType{i}").ToArray()); 
      for (int i = 0; i < types.Length; i++) 
      { 
       typeBuilder.DefineField($"{string.Join("_", names[i])}", genericParams[i], FieldAttributes.Public); 
      } 

      // Create generic type by passed properties 
      var type = typeBuilder.CreateType(); 
      var genericType = type.MakeGenericType(types); 

      ParameterExpression parameter = Expression.Parameter(typeof(T), "MyItem"); 

      // Create nested properties 
      var assignments = genericType.GetFields().Select((prop, i) => Expression.Bind(prop, GetAllNestedMembers(parameter, names[i]))); 
      return Expression.Lambda<Func<T, object>>(Expression.MemberInit(Expression.New(genericType.GetConstructors()[0]), assignments), parameter); 
     } 

     private Expression GetAllNestedMembers(Expression parameter, List<string> properties) 
     { 
      Expression expression = parameter; 
      for (int i = 0; i < properties.Count; ++i) 
      { 
       expression = Expression.Property(expression, properties[i]); 
      } 
      return expression; 
     } 

     private List<string> GetAllNestedMembersName(Expression arg) 
     { 
      var result = new List<string>(); 
      var expression = arg as MemberExpression; 
      while (expression != null && expression.NodeType != ExpressionType.Parameter) 
      { 
       result.Insert(0, expression.Member.Name); 
       expression = expression.Expression as MemberExpression; 
      } 
      return result; 
     } 
    } 

、あなたは現在のソリューション上記のコードで見たように例のために動作しません。それを修正するのは難しいことではありません。

編集:上記のケースが修正されました。今、あなたはclass1->class2->class3->Propery_1

をretriveことができ、それはそれの使い方です:

残念ながら
private class TestClass 
{ 
    public string Arg2 { get; set; } 

    public TestClass Nested { get; set; } 

    public int Id { get; set; } 
} 

var field = new FieldSelector<TestClass>(); 
field.Add(e => e.Arg2); 
field.Add(e => e.Id); 
field.Add(e => e.Nested.Id); 
dynamic cusObj = field.GetSelector().Compile()(new TestClass { Arg2 = "asd", Id = 6, Nested = new TestClass { Id = 79 } }); 
Console.WriteLine(cusObj.Arg2); 
Console.WriteLine(cusObj.Id); 
Console.WriteLine(cusObj.Nested_Id); 

、cusObjはコンパイルタイプでobjectなり、あなたはdynamicとしてそれをマークしていない場合、あなたは彼のpropertisを取得することはできません。あなたはリターンreturn query.Select(field.GetSelector()).ToListAsync()

希望、これは便利ですが呼び出されますときfield.GetSelector()戻りFunc<T, object>、あなたがTask<List<object>>を取得するためところで

、あなたのpublic Task<List<T>> RunQuery<T>は、コンパイルではないでしょう。

+0

ありがとうございました!これはうまく動作します!私はdotnet coreを使用しているため、これを使用する必要があります:AssemblyBuilder.DefineDynamicAssembly – r03

関連する問題