【问题标题】:Applying expression tree for List through LINQ通过 LINQ 为 List 应用表达式树
【发布时间】:2014-06-10 10:22:44
【问题描述】:

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

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

没有为类型“System.String”定义属性 XXXX

代码:

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

【问题讨论】:

  • 请详细说明“它不起作用”。
  • 我已经编辑了我的帖子。请更改投票状态

标签: c# linq azure expression-trees


【解决方案1】:

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

但是,您似乎正试图通过链接它们来获取多个属性。这行不通。我想你想要的是:

  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()ToString() 的任何其他类没有任何不同。因此,除非您有办法使用您正在寻找的定义引用一个类,否则您将无法使用反射访问这些成员(因为它们不存在)。最好只使用 lambda 表达式。

为了更清楚一点,下面是上面匿名类型的类定义(差不多)。

public class <>f__AnonymousType0<T1,T2,T3>
{
    private readonly T1 accountingDocumentNbr;
    private readonly T2 glCompanyCode;
    private readonly T3 documentFiscalYearNbr;

    public T1 AccountingDocumentNbr
    {
        get { return accountingDocumentNbr; }
    }

    public T2 GLCompanyCode
    {
        get { return glCompanyCode; }
    }

    public T3 DocumentFiscalYearNbr
    {
        get { return documentFiscalYearNbr; }
    }

    public <>f__AnonymousType0(T1 accountingDocumentNbr, T2 glCompanyCode, T3 documentFiscalYearNbr)
    {
        this.accountingDocumentNbr = accountingDocumentNbr;
        this.glCompanyCode = glCompanyCode;
        this.documentFiscalYearNbr = documentFiscalYearNbr;
    }

    public override string ToString()
    {
        var builder = new StringBuilder();
        builder.Append("{ AccountingDocumentNbr = ");
        builder.Append(AccountingDocumentNbr);
        builder.Append(", GLCompanyCode = ");
        builder.Append(GLCompanyCode);
        builder.Append(", DocumentFiscalYearNbr = ");
        builder.Append(DocumentFiscalYearNbr);
        builder.Append(" }");
        return builder.ToString();
    }

    public override bool Equals(object value)
    {
        var type = value as <>f__AnonymousType0<T1,T2,T3>;
        return (type != null) && EqualityComparer<T1>.Default.Equals(type.AccountingDocumentNbr, AccountingDocumentNbr) && EqualityComparer<T2>.Default.Equals(type.GLCompanyCode, GLCompanyCode) && EqualityComparer<T3>.Default.Equals(type.DocumentFiscalYearNbr, DocumentFiscalYearNbr);
    }

    public override int GetHashCode()
    {
        int num = 0x7a2f0b42;
        num = (-1521134295*num) + EqualityComparer<T1>.Default.GetHashCode(AccountingDocumentNbr);
        num = (-1521134295*num) + EqualityComparer<T2>.Default.GetHashCode(GLCompanyCode);
        return (-1521134295*num) + EqualityComparer<T3>.Default.GetHashCode(DocumentFiscalYearNbr);
    }
}

【讨论】:

    【解决方案2】:

    我能够解决我的问题。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多