Costruire l'albero delle espressioni usando l'indicizzatore di un parametro

c# expression expression-trees lambda linq

Domanda

Data una classe che ha una proprietà che è un dizionario

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

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

Voglio essere in grado di abbinare i prodotti in un elenco di prodotti in base a criteri che vengono recuperati da un archivio dati nel formato di

Brand == Tyco
Color != Blue

Il mio attuale approccio è di costruire un'espressione dal filtro, e quindi passare quell'espressione come parametro a un LINQ Where metodo chiama così

products = products.Where(myConstructedExpression);

dove myConstructedExpression sarebbe normalmente un'espressione lamda che assomiglia

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

Ho assemblato il seguente codice a scopo di test, ma fallisce sempre la chiamata a lambda.Compile() indipendentemente da ciò che ho provato per aver lasciato l'espressione.

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

Domande

  1. È anche un approccio ragionevole e, in tal caso,
  2. Come costruisco l'espressione sinistra?

Risposta accettata

Quindi per ottenere p => p.Attributes["Brand"] <someoperator> "Tyco" , puoi farlo.

Il "trucco", per lavorare con i tipi indicizzati, è usare la loro proprietà Item (potresti anche lavorare con il metodo 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) });

MODIFICARE

la versione con il test IDictionary.ContainsKey(<value>)

davvero passo dopo passo, ma penso che ciò chiarisca le cose all'inizio.

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


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché