通过LINQ为List应用表达式树

时间:2013-05-03 20:14:05

标签: c# linq azure expression-trees

我想为我的List Collection应用动态项目。用户将选择注释的列,但列表的属性。我想从LINQ语句中获取列的子集。我想使用Dynamic LINQ。 any1可以提供如何使用表达式树实现项目。我想在Azure表存储上应用动态投影..

我尝试实现下面的代码,它不起作用,它在读取属性时抛出异常:

  

属性XXXX未定义类型'System.String'

代码:

DataMovementDataContext dbMovement = new DataMovementDataContext();
var entity = dbMovement.ListofAccountingDocs2_1075s.AsQueryable();
Type type = entity.ElementType;
var entityParam = Expression.Parameter(entity.ElementType, "row");
Expression expr = entityParam;
string[] props = "AccountingDocumentNbr,GLCompanyCode,DocumentFiscalYearNbr".Split(',');
var epr =  GenerateMemberExpression<ListofAccountingDocs2_1075, string>("Name");

foreach (string prop in props)
{
    // use reflection (not ComponentModel) to mirror LINQ
    PropertyInfo pi = type.GetProperty(prop);
    expr = Expression.Property(expr, pi);
   //  type = pi.PropertyType; //Property 'System.String GLCompanyCode' is not defined for type 'System.String'
}

// row => row.Property
// var columnLambda = Expression.Lambda(  Expression.Property(entityParam, "GLCompanyCode"), entityParam);
var columnLambda = Expression.Lambda(Expression.Property(expr, "AccountingDocumentNbr,GLCompanyCode"), entityParam);

// Items.Select(row => row.Property)
var selectCall = Expression.Call(typeof(Queryable), "Select", new Type[] { entity.ElementType, columnLambda.Body.Type }, entity.Expression, columnLambda);

// Items.Select(row => row.Property).Distinct
var distinctCall = Expression.Call(typeof(Queryable), "Distinct", new Type[] { typeof(string) }, selectCall);

// colvalue => colvalue
var sortParam = Expression.Parameter(typeof(string), "AccountingDocumentNbr");
var columnResultLambda = Expression.Lambda(sortParam, sortParam);

// Items.Select(row => row.Property).Distinct.OrderBy(colvalue => colvalue)
var ordercall = Expression.Call(typeof(Queryable), "OrderBy",
           new Type[] { typeof(string), columnResultLambda.Body.Type },
           distinctCall, columnResultLambda);

var result =  entity.Provider.CreateQuery(ordercall);
foreach (var item in result)
{
    Console.Write(item);
}

2 个答案:

答案 0 :(得分:3)

如果您发布了正在使用的模型的类定义,那将会更容易。

但是,看起来您正试图通过链接来获取多个属性。这不起作用。我想你想要的是:

  new { 
      AccountingDocumentNbr = document.AccountingDocumentNbr, 
      GLCompanyCode = document.GLCompanyCode , 
      DocumentFiscalYearNbr = document.DocumentFiscalYearNbr
  };

foreach (string prop in props)循环实际上是您需要的东西:

  document.AccountingDocumentNbr.GLCompanyCode.DocumentFiscalYearNbr

现在,document.AccountingDocumentNbr似乎是string,因为您收到Property 'System.String GLCompanyCode' is not defined for type 'System.String'错误。当你这样看时,错误是有道理的...... System.String没有GLCompanyCode属性,你正在构建一个链式属性表达式,期望它有一个。还会发生什么?

除非您的解决方案中已存在该类型的实例,否则您无法获取它所针对的匿名对象。这是因为匿名类型不是动态类型。它们可能看起来像它,但它们实际上是internal types compiled into the assembly,并且它们的功能与具有相同成员和Equals(object obj)GetHashCode()和{的自定义覆盖的任何其他类的功能完全不同。 {1}}。因此,除非您有方法使用您要查找的定义引用类,否则您将无法使用反射访问这些成员(因为它们不存在)。使用lambda表达式会好得多。

为了更加清晰一点,以下是上面匿名类型的类定义(几乎就是这样)。

ToString()

答案 1 :(得分:0)

我能够解决我的问题。

public static Expression<Func<ListofAccountingDocs2_1075, ListofAccountingDocs2_1075>> BuildExpression(string parameters)
{  
    dynamic test = new ExpandoObject();
    var sourceMembers = typeof(ListofAccountingDocs2_1075).GetProperties();

    string[] selectparams =  parameters.Split(',');//property names are comma seperated

    foreach (var item in selectparams)
      {         
          ((IDictionary<string, object>)test).Add(item,string.Empty); 
      }
    IDictionary<string,object> test2 = new Dictionary<string,object>();

    List<PropertyInfo> destinationProperties = new List<PropertyInfo>();

    foreach (var item in ((IDictionary<string, object>)test))
    {
      var selectedColumn  = typeof(ListofAccountingDocs2_1075).GetProperties().FirstOrDefault(k =>
         k.Name.Equals(item.Key));

      if (selectedColumn != null)
          destinationProperties.Add(selectedColumn);
    }

    var name = "src";

    var parameterExpression = Expression.Parameter(typeof(ListofAccountingDocs2_1075), name);

    return Expression.Lambda<Func<ListofAccountingDocs2_1075, ListofAccountingDocs2_1075>>(
        Expression.MemberInit(
            Expression.New(typeof(ListofAccountingDocs2_1075)),
            destinationProperties.Select(k => Expression.Bind(k,
                Expression.Property(
                    parameterExpression, k.Name)
                )
            ).ToArray()
            ),
        parameterExpression
    );

}