Aus der Praxis – Dynamic LINQ
Seit den ersten Tagen von LINQ im .Net Framework bin ich von dieser Technologie absolute begeistert und präferiere sie wann immer ich in meinen Projekten Datenabfragen machen muss. Nun kann es in der Praxis allerdings durchaus vorkommen, dass eine Abfrage dynamisch zur Laufzeit zusammen gesetzt werden muss. Ein gängiges Szenario ist bspw. das Filtern von Tabellen. Der Anwender wählt eine oder mehrere Spalten aus und gibt die Filterwerte ein.
Nun wäre es ja Blödsinn darauf mit einer immense Logik zu reagieren. Unter dem Motto – wenn Spalte X zu filtern ist, dann gehe auf das Property X wo der Wert dem Filterkriterium genügt. Wenn die Spalte exakt wie das Property heißen würde, könnte man Reflection verwenden; aber das verträgt sich auch nicht mit LINQ.
Und was nun?! Doch wieder zusammengesetztes TSQL gegen die Datenbank feuern?!
Nein!
Für diesen Fall gibt es von Microsoft bereits eine Bibliothek mit der LINQ-Abfragen dynamisch zusammen gesetzt werden können.
Unterm Strich ist es mit Dynamic LINQ möglich die Where-Klausel als String zu definieren.
Beispiel:
var query = northwind.Products
.Where(“CategoryID = 3 AND UnitPrice > 3”)
.OrderBy(“SupplierID”);
oder
.Where(“MyColumn.Contains(@0)”, myArray)
Dynamic LINQ funktioniert hervorragen. Sowohl Stabilität als auch Performanz lassen keine Wünsche übrig.
Allerdings handelt es sich um eine zusätzliche Komponente die man mit in das Projekt einbinden muss. Ich hatte tatsächlich schon den Fall, dass ein Projekt nur mit nativen .Net-Bordmitteln umgesetzt werden sollte. Aus diesem Grund habe ich geprüft, was es für Alternativen diesbezüglich gibt. Und es in der Tat möglich seinen eigenen kleinen “Dynamic LINQ”-Mechanismus zu implementieren. Ermöglicht wird dies durch den immensen Funktionsumfang der Klasse Expression aus dem Namespace System.Linq. (http://msdn.microsoft.com/de-de/library/system.linq.expressions.expression.aspx)
Hier der Code für die Methode “DynWhere”:
using System;
using System.Linq;
using System.Linq.Expressions;
namespace demo1
{
public enum WhereOperation { Equal, NotEqual, Contains };
public static class Extensions
{
public static IQueryable<T> DynWhere<T>(this IQueryable<T> query, string column, object value, WhereOperation operation)
{
if (string.IsNullOrEmpty(column))
return query;
ParameterExpression parameter = Expression.Parameter(query.ElementType, “parm”);
MemberExpression memberAccess = null;
foreach (var property in column.Split(‘.’))
memberAccess = MemberExpression.Property
(memberAccess ?? (parameter as Expression), property);
//change param value type
//necessary to getting bool from string
ConstantExpression filter = Expression.Constant
(
Convert.ChangeType(value, memberAccess.Type)
);
//switch operation
Expression condition = null;
LambdaExpression lambda = null;
switch (operation)
{
case WhereOperation.Equal:
condition = Expression.Equal(memberAccess, filter);
lambda = Expression.Lambda(condition, parameter);
break;
case WhereOperation.NotEqual:
condition = Expression.NotEqual(memberAccess, filter);
lambda = Expression.Lambda(condition, parameter);
break;
//string.Contains()
case WhereOperation.Contains:
condition = Expression.Call(memberAccess,
typeof(string).GetMethod(“Contains”),
Expression.Constant(value));
lambda = Expression.Lambda(condition, parameter);
break;
}
MethodCallExpression result = Expression.Call(
typeof(Queryable), “Where”,
new[] { query.ElementType },
query.Expression,
lambda);
return query.Provider.CreateQuery<T>(result);
}
}
}
Fazit:
Es ist durchaus möglich einen eigenen Mechanismus für dynamische LINQ-Abfragen zu implementieren. Es ist nur deutlich aufwendiger und wird vermutlich nie den Funktionsumfange der Bibliothek von Microsoft erreichen! 🙂
Aus diesem Grund würde ich immer zur Dynamic LINQ Library raten.