Verwenden eines LINQ ExpressionVisitor zum Ersetzen primitiver Parameter durch Eigenschaftsreferenzen in einem Lambda-Ausdruck
Ich bin dabei, eine Datenschicht für einen Teil unseres Systems zu schreiben, die Informationen zu automatisierten Jobs protokolliert, die jeden Tag ausgeführt werden - Name des Jobs, Laufzeit, Ergebnis usw.
Ich spreche mit der Datenbank mithilfe von Entity Framework, versuche jedoch, diese Details vor übergeordneten Modulen zu verbergen, und möchte nicht, dass die Entitätsobjekte selbst verfügbar gemacht werden.
Ich möchte jedoch meine Benutzeroberfläche in Bezug auf die Kriterien für die Suche nach Jobinformationen sehr flexibel gestalten. Eine Benutzeroberfläche sollte es dem Benutzer beispielsweise ermöglichen, komplexe Abfragen wie "Gib mir alle Jobs mit dem Namen" Hallo "auszuführen, die zwischen 10:00 Uhr und 11:00 Uhr ausgeführt wurden und fehlgeschlagen sind." Offensichtlich sieht dies nach einem dynamisch aufgebauten Job ausExpression
Bäume.
Ich möchte also, dass meine Datenschicht (Repository) LINQ-Ausdrücke vom Typ akzeptiertExpression<Func<string, DateTime, ResultCode, long, bool>>
(Lambda-Ausdruck) und dann hinter den Kulissen dieses Lambda in einen Ausdruck konvertieren, der meinem Entity Framework entsprichtObjectContext
kann als Filter in einem verwendet werdenWhere()
Klausel.
Kurz gesagt, ich versuche, einen Lambda-Ausdruck vom Typ zu konvertierenExpression<Func<string, DateTime, ResultCode, long, bool>>
zuExpression<Func<svc_JobAudit, bool>>
, wohersvc_JobAudit
ist das Entity Framework-Datenobjekt, das der Tabelle entspricht, in der Jobinformationen gespeichert sind. (Die vier Parameter im ersten Delegaten entsprechen dem Namen des Jobs, dem Ausführungszeitpunkt, dem Ergebnis und der Dauer in MS.)
Ich habe mit dem sehr gute Fortschritte gemachtExpressionVisitor
klasse bis ich gegen eine mauer stoße und eineInvalidOperationException
mit dieser Fehlermeldung:
Beim Aufruf von 'VisitLambda' muss beim Umschreiben eines Knotens vom Typ 'System.Linq.Expressions.ParameterExpression' ein Nicht-Null-Wert desselben Typs zurückgegeben werden. Alternativ können Sie 'VisitLambda' überschreiben und so ändern, dass keine Kinder dieses Typs besucht werden.
Ich bin völlig ratlos. Warum zum Teufel kann ich keine Ausdrucksknoten konvertieren, die auf Parameter verweisen, in Knoten, die auf Eigenschaften verweisen? Gibt es einen anderen Weg, dies zu tun?
Hier ist ein Beispielcode:
namespace ExpressionTest
{
class Program
{
static void Main(string[] args)
{
Expression<Func<string, DateTime, ResultCode, long, bool>> expression = (myString, myDateTime, myResultCode, myTimeSpan) => myResultCode == ResultCode.Failed && myString == "hello";
var result = ConvertExpression(expression);
}
private static Expression<Func<svc_JobAudit, bool>> ConvertExpression(Expression<Func<string, DateTime, ResultCode, long, bool>> expression)
{
var newExpression = Expression.Lambda<Func<svc_JobAudit, bool>>(new ReplaceVisitor().Modify(expression), Expression.Parameter(typeof(svc_JobAudit)));
return newExpression;
}
}
class ReplaceVisitor : ExpressionVisitor
{
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof(string))
{
return Expression.Property(Expression.Parameter(typeof(svc_JobAudit)), "JobName");
}
return node;
}
}
}