每周源代码48-DynamicQueryable让自定义LINQ更简单

[原文发表地址]The Weekly Source Code 48 - DynamicQueryable makes custom LINQ expressions easier

[原文发表时间] 2011-01-27 01:52 AM

注意: 本文的另一个题目可以是:“每周源代码 48 :让每周源代码47吸入更少。”

注意: 这不是语言功能!在C# 和VB上都能工作。

上周,为解决一种元编程问题,我写了一篇关于Dynamic Linq Query Generation的博文。我有个用ASP.NET 动态数据的网站,想对一些数据做一个LINQ查询。但是,因为我在创建一个模板,对编译的时间无法足够了解而不能写一个合适的LINQ查询,所以我需要动态的创建我的LINQ。

绝对不要离开,精彩的在后边。

运行时,我试图有效生成这个:

 Items.Select(row => row.Property).Distinct.OrderBy(colvalue => colvalue)

Tatham Oddie的帮助下,我成功地用这种次优方式完成了:

protected void Page_Init(object sender, EventArgs e)
{
var items = Column.Table.GetQuery();
var entityParam = Expression.Parameter(Column.Table.EntityType, "row");

           // row => row.Property
var columnLambda = Expression.Lambda(Expression.Property(entityParam, Column.EntityTypeProperty), entityParam);

// Items.Select(row => row.Property)
var selectCall = Expression.Call(typeof(Queryable), "Select", new Type[] { items.ElementType, columnLambda.Body.Type }, items.Expression, columnLambda);

// Items.Select(row => row.Property).Distinct
var distinctCall = Expression.Call(typeof(Queryable), "Distinct", new Type[] { Column.EntityTypeProperty.PropertyType }, selectCall);

// colvalue => colvalue
var sortParam = Expression.Parameter(Column.EntityTypeProperty.PropertyType, "sortValue");
var columnResultLambda = Expression.Lambda(sortParam, sortParam);

// Items.Select(row => row.Property).Distinct.OrderBy(colvalue => colvalue)
var ordercall = Expression.Call(typeof(Queryable), "OrderBy",
new Type[] { Column.EntityTypeProperty.PropertyType, columnResultLambda.Body.Type },
distinctCall, columnResultLambda);
var result = items.Provider.CreateQuery(ordercall);
foreach (var item in result)
{
if (item != null) DropDownList1.Items.Add(item.ToString());
}
}
     

“次优”虽然能工作但却是蹩脚的,因它难以阅读。难道我应该去忍受吗?

幸运的是,来自ASP.NET团队的Marcin决定打破他明确保持沉默的博客誓言(持续了18个月,至少)来拯救我。

Marcin指出,Ms-PL(什么人知道这个呢?)下发布的2006有个叫DynamicQueryable的样本。实际上,现在你的硬盘驱动上就有这个东西。它在你的VS安装目录的Samples\1033\CSharpSamples.zip\LinqSamples\DynamicQuery\DynamicQuery in your 下。

实际上,2008年一月,His Gu-ness博客上谈到过,给出这个VB例子:

Dim Northwind As new NorthwindDataContext
Dim query = From p In Northwind.Products
Where p.CategoryID = 2 And UnitPrice > 3
Order By p.SupplierID
Select p
GridView1.DataSource = query
GridView1.DataBind()

不过,用DynamicQuery库你可以这样表达同样的事情,允许更多的动态化(有这个词语吗?)

Dim Northwind As new NorthwindDataContext
Dim query = Northwind.Products
.Where("CategoryID=2 And p.UnitPrice>3")
.OrderBy("SupplierID")
GridView1.DataSource = query
GridView1.DataBind()

再次,如果你不是提前知道每个输入,这个用起来很好。Marcin说;

DynamicQueryable十分强大,包括下面这些

· 对任何LINQ提供者的基于字符串的动态查询( Where , Select , OrderBy , Take , Skip , GroupBy , Any , Count扩展方法的后期绑定版本)

· 基于字符串的迷你表达语言(像下面例子中的“it ”标识符),包括复合条件句和所有的操作符。

· 动态为预测创建类

现在Marcin可以把我上述一堆废话重写成很好的四小行。DynamicQueryable的魔力是"var result ="行。

protected void Page_Init(object sender, EventArgs e)
{
var items = Column.Table.GetQuery();
var result = items.Select(Column.EntityTypeProperty.Name).Distinct().OrderBy("it");
foreach (var item in result)
{
if (item != null) DropDownList1.Items.Add(item.ToString());
}
}

ScottGu还提到Joseph 和 Ben Albahari,C# 3.0 In a Nutshell一书的作者,以及他们关于构建安全类型的谓词法的高深的帖子。他们的PredicateBuilder 在 LINQKit 扩展库里是免费的,甚至你更深入这个话题,它可以帮助你。

Oh,真的,停下你手头的事情,去下载LINQPad,这是Albahari给我们所有人的最好的礼物。然后,谢谢他们,告诉他们它们有多么棒。

祝大家使用愉快!