可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
i want to replace operator(==, >= ,>...) in the clause where of linq lambda with parameter passed in method
the method:
public IEnumerable<Localisation> GetByFiltre(string filter, string valeurDate1)
/*
filter has the value of an operator:
>
==
!=
>=
<=
*/
DateTime dt = Convert.ToDateTime(valeurDate1);
var mod = from o in new GpsContext().Locals.Where(loc => loc.Date == dt)
i want to replace == in the clause where with the parameter filter
to obtain something like this
var mod = from o in new GpsContext().Locals.Where(loc => loc.Date filter dt)
any body knows how to make it work ?
回答1:
I think it's better to make dictionary out of string filters and corresponding delegates.
class YourClass
{
static readonly Dictionary<string, Func<DateTime, DateTime, bool>> s_filters = new Dictionary<string, Func<DateTime, DateTime, bool>>
{
{ ">", new Func<DateTime, DateTime, bool>((d1, d2) => d1 > d2) }
{ "==", new Func<DateTime, DateTime, bool>((d1, d2) => d1 == d2) }
{ "!=", new Func<DateTime, DateTime, bool>((d1, d2) => d1 != d2) }
{ ">=", new Func<DateTime, DateTime, bool>((d1, d2) => d1 >= d2) }
{ "<=", new Func<DateTime, DateTime, bool>((d1, d2) => d1 <= d2) }
};
public IEnumerable<Localisation> GetByFiltre(string filter, string valeurDate1)
{
...
DateTime dt = Convert.ToDateTime(valeurDate1);
var filterDelegate = s_filters[filter];
var mod = from o in new GpsContext().Locals.Where(loc => filterDelegate(loc.Date,dt));
...
}
}
回答2:
There is a good library for parsing strings into Lamdba expressions described here
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
and downloadable here
http://msdn2.microsoft.com/en-us/vcsharp/bb894665.aspx
It has a pretty good expression syntax that lets you express quite a lot of different queries and operations.
Be aware though that depending on the query, you may lose some type safety. Where operations are OK, but any kind of projection where the Select lambda is parsed from a string cannot be inferred by the compiler. This measn you end up with non-generic IQueryables rather than generically typed ones. Sometimes this is OK, but it does prevent you from using generic extension methods later on in the query.
Edit to clarify situation with non-generic query operations: The library contains a set of non-generic versions of the query extension methods that take string representations of expressions and operate on non-generic IQueryable. If you look at the code, it is pretty easy to see how to write these if the one you want isn't there. For example, I needed to do a non-generic Join and it only took a couple of hours.
回答3:
I found a solution to your problem that works like this:
var test = dataContext.Interactions.DynamicWhere<Interaction,DateTime>("Created_Month", ExpressionType.LessThan, DateTime.Now);
You can use any ExpressionType
- equals, less than, greater than, etc. and it will get translated to T-SQL if possible (so the filtering will be done on the server). It will also work in-memory on IEnumerables
.
Here's the code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace WindowsFormsApplication1
{
public static class GenericFilterExtension
{
public static IQueryable<TRow> DynamicWhere<TRow,TColumn>(this IQueryable<TRow> input, string field, ExpressionType binaryOperator, TColumn value)
{
var exp = MakeWhereLambda<TRow, TColumn>(field, binaryOperator, value) as Expression<Func<TRow, bool>>;
return input.Where(exp);
}
public static IEnumerable<TRow> DynamicWhere<TRow, TColumn>(this IEnumerable<TRow> input, string field, ExpressionType binaryOperator, TColumn value)
{
var exp = MakeWhereLambda<TRow, TColumn>(field, binaryOperator, value).Compile() as Func<TRow, bool>;
return input.Where(exp);
}
private static LambdaExpression MakeWhereLambda<TRow, TColumn>(string field, ExpressionType binaryOperator, TColumn value)
{
var param = Expression.Parameter(typeof(TRow), "n");
var op = Expression.MakeBinary(binaryOperator, Expression.Property(param, field), Expression.Constant(value));
return Expression.Lambda(op, new ParameterExpression[] { param });
}
}
}
回答4:
You could pass in the function that is your where clause, e.g.
public IEnumerable<Localisation> GetByFiltre(Func<IEnumerable<localisation>, IEnumerable<localisation>> whereClause)
{
/*
filter has the value of an operator:
>
==
!=
>=
<=
*/
DateTime dt = Convert.ToDateTime(valeurDate1);
var mod = whereClause(new GpsContext().Locals);
}
And call it with:
GetByFiltre(f => f.Where(d => d.Date > SomeDate));
回答5:
And the filter should contain "==", ">=" etc? You can analyse filter string in a traditional way:
var mod = from o in new GpsContext().Locals.Where(loc =>
{
switch(filter)
{
case "==":
return loc.Date == dt;
case ">=":
return loc.Date >= dt;
// ...
}
})