Sto seguendo questa risposta SO per convertire le espressioni lambda in sintassi SQL parziale.
Tuttavia, ho problemi durante l'analisi dell'espressione per Contains
. Ho aggiunto un metodo:
private bool ParseContainsExpression(MethodCallExpression expression)
{
MemberExpression member = (MemberExpression)expression.Arguments[0];
var methodInfo = typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) });
//TODO check if list contains value
return false;
}
Dato che sono totalmente nuovo alle espressioni, non so da dove ottenere il nome e il valore della proprietà, e l'elenco che contiene i valori che voglio controllare. Dove sono conservate queste proprietà e questi valori all'interno dell'espressione?
Fondamentalmente è necessario elaborare il metodo , l' oggetto ( espressione che rappresenta l'istanza per le chiamate del metodo di istanza o null per le chiamate al metodo statico ) e gli argomenti ( una raccolta di espressioni che rappresentano argomenti del metodo chiamato ) della classe MethodCallExpression .
In particolare per Contains
, è necessario evitare (o elaborare in modo diverso se necessario) il metodo string.Contains
e anche gestire metodi static
come Enumerable.Contains
e metodi di istanza come ICollection<T>.Contains
, List<T>.Contains
etc . Per ottenere i valori della lista (quando possibile), devi trovare una sorta di espressione costante.
Ecco un esempio:
private bool ParseContainsExpression(MethodCallExpression expression)
{
// The method must be called Contains and must return bool
if (expression.Method.Name != "Contains" || expression.Method.ReturnType != typeof(bool)) return false;
var list = expression.Object;
Expression operand;
if (list == null)
{
// Static method
// Must be Enumerable.Contains(source, item)
if (expression.Method.DeclaringType != typeof(Enumerable) || expression.Arguments.Count != 2) return false;
list = expression.Arguments[0];
operand = expression.Arguments[1];
}
else
{
// Instance method
// Exclude string.Contains
if (list.Type == typeof(string)) return false;
// Must have a single argument
if (expression.Arguments.Count != 1) return false;
operand = expression.Arguments[0];
// The list must be IEnumerable<operand.Type>
if (!typeof(IEnumerable<>).MakeGenericType(operand.Type).IsAssignableFrom(list.Type)) return false;
}
// Try getting the list items
object listValue;
if (list.NodeType == ExpressionType.Constant)
// from constant value
listValue = ((ConstantExpression)list).Value;
else
{
// from constant value property/field
var listMember = list as MemberExpression;
if (listMember == null) return false;
var listOwner = listMember.Expression as ConstantExpression;
if (listOwner == null) return false;
var listProperty = listMember.Member as PropertyInfo;
listValue = listProperty != null ? listProperty.GetValue(listOwner.Value) : ((FieldInfo)listMember.Member).GetValue(listOwner.Value);
}
var listItems = listValue as System.Collections.IEnumerable;
if (listItems == null) return false;
// Do whatever you like with listItems
return true;
}
La tua implementazione è molto diversa dalla risposta di esempio. Devi davvero ereditare da ExpressionVisitor
modo da poter analizzare correttamente l'albero.
Prendiamo questa espressione per un esempio:
var myList = new List<string> { "A" };
Expression<Func<string, bool>> a = (s) => myList.Contains(s);
ParseContainsExpression(a.Body as MethodCallExpression);
private bool ParseContainsExpression(MethodCallExpression expression)
{
expression.Object; //myList
expression.Arguments[0]; //s
return false;
}
Nota comunque, queste sono ancora espressioni , non sono ancora valori attuali. È necessario richiamare l'espressione per ottenere i valori.
Tuttavia, nel nostro caso - myList
è in realtà una myList
ConstantExpression
. Quindi possiamo fare questo:
((expression.Object as MemberExpression).Expression as ConstantExpression).Value; //myList
Che ci restituisce la lista originale. Si noti che si tratta di un accesso al campo , perché l'espressione è compilata in una chiusura, che inserisce myList
come un campo nella classe di chiusura. Come puoi vedere, dobbiamo fare un sacco di ipotesi sul tipo di espressione che stiamo gestendo (che è un accesso al campo e quindi un'espressione costante). Hai davvero bisogno di un'implementazione completa per i visitatori per farlo correttamente (che descrive la risposta collegata)