【问题标题】:Building Expression Tree Using a Parameter's Indexer使用参数的索引器构建表达式树
【发布时间】:2013-09-06 17:28:19
【问题描述】:

给定一个具有字典属性的类

public class Product
{
    public Dictionary<string, string> Attributes { get { return attributes; } }

    private Dictionary<string, string> attributes = new Dictionary<string, string>();
}

我希望能够根据从数据存储中检索到的标准匹配产品列表中的产品,该标准采用以下格式

Brand == Tyco
Color != Blue

我目前的方法是从过滤器构造一个表达式,然后将该表达式作为参数传递给 LINQ Where 方法调用,就像这样

products = products.Where(myConstructedExpression);

myConstructedExpression 通常是一个看起来像这样的 lamda 表达式

p => p.Attributes[attribute] == value

我已经组装了以下代码用于测试目的,但无论我为他的左表达尝试了什么,它总是无法调用lambda.Compile()

Dictionary<string, ExpressionType> expressionType = new Dictionary<string, ExpressionType>();
expressionType.Add("==", ExpressionType.Equal);
expressionType.Add("!=", ExpressionType.NotEqual);

string filter = "Brand == Tyco";
string[] fields = filter.Split(' ');
string attribute = fields[0];
string op = fields[1];
string value = fields[2];

Product product = new Product();
product.Attributes.Add("Brand", "Tyco"); 

var parameter = Expression.Parameter(typeof(Product), "p");
var left = /***** THIS IS WHAT I AM FAILING TO CONSTRUCT PROPERLY ********/
var right = Expression.Constant(value);
var operation = Expression.MakeBinary(expressionType[op], left, right);
var lambda = Expression.Lambda<Func<Product, bool>>(operation, parameter);

var result = lambda.Compile()(product);

问题

  1. 这是否是一种合理的方法,如果是,
  2. 如何构造左表达式?

【问题讨论】:

  • 你为什么要使用表达式树?为什么 lambda 还不够?
  • 因为我不知道过滤器会是什么。具体来说,我不知道运营商会是什么。我必须在运行时决定它是否将是 ==、!=、、= 等。而且,我不能将任意运算符传递给 lambda 表达式。

标签: c# linq lambda expression expression-trees


【解决方案1】:

所以要得到p =&gt; p.Attributes["Brand"] &lt;someoperator&gt; "Tyco",你可以这样做。

使用索引类型的“技巧”是使用它们的Item 属性(您也可以使用get_item 方法)

var parameter = Expression.Parameter(typeof(Product), "p");
Expression left = Expression.Property(parameter, "Attributes");
left = Expression.Property(left, "Item", new Expression[] { Expression.Constant(attribute) });

编辑

带有IDictionary.ContainsKey(&lt;value&gt;) 测试的版本

确实是一步一步来的,但我认为这让事情一开始就更清楚了。

//left part of lambda, p
var parameter = Expression.Parameter(typeof(Product), "p");
//right part
//p.Attributes
Expression left = Expression.Property(parameter, "Attributes");

var method = typeof(IDictionary<string, string>).GetMethod("ContainsKey");
//p.Attributes.ContainsKey("Brand");
Expression containsExpression = Expression.Call(left, method, Expression.Constant(attribute));
//p.Attributes.Item["Brand"]
Expression keyExpression= Expression.Property(left, "Item", new Expression[] { Expression.Constant(attribute) });
//"Tyco"
var right = Expression.Constant(value);
//{p => IIF(p.Attributes.ContainsKey("Brand"), (p.Attributes.Item["Brand"] == "Tyco"), False)}
Expression operation = Expression.Condition(
                           containsExpression,
                           Expression.MakeBinary(expressionType[op], keyExpression, right), 
                           Expression.Constant(false));
var lambda = Expression.Lambda<Func<Product, bool>>(operation, parameter);

【讨论】:

  • 没有。 Brand 是 Attributes 字典中的一个键。 Attributes 字典是 Product 上的一个属性。
  • @JohnKraft 所以一个带有 ContainsKey 测试的版本。
  • 谢谢!我不知道如何将索引作为属性表达式。我没有意识到我可以从另一个属性表达式中创建一个属性表达式。将 left 重新分配给索引器属性表达式是我的代码中唯一缺少的部分。
  • 顺便说一句,索引器并不总是必须被称为Item。例如,对于string,它称为Chars。但此代码适用于 DictionaryIDictionary
  • @RaphaëlAlthaus 不,string.Chars 的吸气剂称为get_Chars。我认为最通用的方法是使用GetDefaultMembers()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多