前面介绍了Linq的三个方面应用:Linq to SQL, Linq to XML和Linq to Object,这篇介绍一下动态Linq的实现方式及应用场景。
命名空间:
System.Linq;
System.Linq.Expressions;
应用Linq的时候,我们都知道仅仅须要Lambda表达式即可,但有些场景仅仅仅仅使用Data Model的字段名操作是不够的或者不方便的。
场景1:如果我们须要拼接Where条件进行查询,一种方式能够拼接IQueryable的表达式。但我想像写SQL语句那样直接拼接一个Where条件即可,Linq要怎么实现?
场景2:如果我们想要一个列表,这个列表能够按每一个表头来排序,我们把表头当作參数传给排序函数,在函数内部该怎么实现呢?能够逐一枚举对照,针对不同的列写不同的Linq语句,但代码非常冗余。用传统方式依据动态字段名怎么实现?
以下来说说Linq的还有一种应用方式: 动态Linq,使用Linq.Expressions. 场景1, 我仅仅想用Where拼接(表名參数)就完毕操作,以下看看实现。以下全部的Demo仅仅是应用于Linq to SQL, 假设是Entity Framework会有差异.
DataClasses1DataContext dbContext = new DataClasses1DataContext();
public string dynamicLinq(int id = 50)
{
IQueryable<DataListForDemo> dLinq = dbContext.DataListForDemos;
ParameterExpression paraExpr = Expression.Parameter(typeof(DataListForDemo), "data");
MemberExpression propExpr = Expression.Property(paraExpr, typeof(DataListForDemo).GetProperty("ID"));
BinaryExpression filter = Expression.LessThan(propExpr, Expression.Constant(id));
LambdaExpression lambdaWhere = Expression.Lambda(filter, paraExpr);
MethodCallExpression Where = Expression.Call(typeof(Queryable),
"Where",
new Type[] { typeof(DataListForDemo) },
Expression.Constant(dLinq),
lambdaWhere
);
var data0 = dLinq.AsQueryable().Provider.CreateQuery(Where);
DbCommand comm = dbContext.GetCommand(data0);
dbContext.Log(comm.CommandText);
return comm.CommandText;
}
上面是各种Linq.Expression的类, 用ParameterExpression定义參数也就是要操作的实体对象, 用PropertyExpression定义属性也就是要操作的字段, 用BinaryExpression定义条件查询的表达式也就是Where条件, 用LambdaExpression定义Lambda表达式也就是IQueryable对象, 最后一步就是来完毕调用. Call方法是来定义你的表达式方法, 如: Where, Select, OrderBy, GroupBy, All, Any,
Equal等等方法, 有哪一种动态需求就写哪一种方法, 这个在MSDN上面没有太多实例, 只是网上能够查到非常多.
上面返回的是生成的SQL语句, SQL语句是这种:
SELECT [t0].[ID], [t0].[col1], [t0].[col2], [t0].[col3], [t0].[col5], [t0].[col4]
FROM [dbo].[DataListForDemo] AS [t0]
WHERE [t0].[ID] < @p0
对比生成的SQL语句和Expression的表达式就非常easy理解Linq是怎么实现的和怎么工作的. 那么有些人会问, IQueayable和IEnuerable的对象都会带有Linq的表达式而并非单独的方法通过传參数实现, 要实现这样的方式那么就得提一下什么是扩展方法以及扩展方法怎么实现. 在C#里面要扩展某个对象的方法能够override基类方法, 可是像string, iqueryable等这样的对象怎么扩展它们的方法呢? Override基类当然不行, 这时就要用thiskeyword,也是this的还有一种应用方式.
使用扩展方法, 首先写一个静态类, 在静态类里面定义一个静态方法, 静态方法里面第一个參数以this開始, 第一个參数也就是你要扩展的系统对象.
如:
public static class DynamicQueryable
{
//扩展IQueryable对象的方法
public static IQueryable Where(this IQueryable source, string predicate, params object[] values)
{
if (source == null) throw new ArgumentNullException("source");
if (predicate == null) throw new ArgumentNullException("predicate");
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values);
return source.Provider.CreateQuery(
Expression.Call(
typeof(Queryable), "Where",
new Type[] { source.ElementType },
source.Expression, Expression.Quote(lambda)));
}
public static LambdaExpression ParseLambda(Type itType, Type resultType, string expression, params object[] values)
{
return ParseLambda(new ParameterExpression[] { Expression.Parameter(itType, "") }, resultType, expression, values);
}
}
上面就是扩展IQueryabler方法, 所以IQueryable类型的全部对象都有了动态Where的功能, 我不知道为什么Microsoft团队没有把这个功能加上, 而仅仅是提供了Expressions类, 加上这些动态表达之后Linq功能会很强壮.
来看看调用:
public string SelectDynamic(int id = 0)
{
DataListForDemo model = dbContext.DataListForDemos.Where("ID = " + id.ToString()).SingleOrDefault();
return model.ID.ToString();
}
如今非常明显的一个变化是Where里面能够仅仅写一个拼接的where条件了, 并且是一个字符串, 这就是大多数程序猿想到的东东吧!
Linq里面全部已经存在的方法都能够通过这样的方式扩展和实现动态化, 很多其它的实现方式能够Google, 建议使用Google, 英文文章有的写得很透彻,并且资源丰富.