LINQ - Custom LINQ Provider (IQueryable, IQueryProvider)
[ 2007-10-20 00:47:10 | 作者: yuhen ]
在博客堂看到 Kevin Halverson 的文章 《如何实现IQueryable (Kevin Halverson)》(VBCTI 翻译),意识到自己还差一堂功课没补上。
在前面分析 LINQ to SQL 的文章中,我们已经明白要实现一个自定义的 LINQ provider,需要实现两个接口 —— IQueryable<T> 和 IQueryProvider。其中 IQueryable<T> 是主要的外部操作接口,被 LINQ 扩展方法(System.Linq.Queryable)调用,用于保持源数据和查询状态;IQueryProvider 则是整个算法核心,分解表达式(Expression), "计算" 并 "筛选" 出我们期望的结果集(IEnumerable<T>)。
看看两个接口在 MSDN 中的说明。
IQueryable<T>
IQueryProvider
首先我们创建一个实现了 IQueryable<T> 的对象,这个对象对象内部包含了一个 T[] Data 数据源。
接下来,我们编写 MyQueryProvider(LINQ Provider) 类型。为了和 MyQueryable<T> 交互,我们通过构造方法传递了对象引用。
最后,我们开始测试一个作坊式的 "发动机",看看效果。
输出:
4
5
还行,如果我们能补全表达式计算代码,应该还是个不错的东西。
依照惯例,我们会进行反编译,并试图分析一下该演示的执行流程。
(1) 调用代码很好理解,首先构造表达式树,然后调用扩展方法 Queryable.Where()。
(2) 在 Queryable.Where() 中 MyQuery<T>.Provider 和 MyQuery<T>.Expression 被调用,MyQuery<T>.Expression 作为参数传递给 MyQueryProvider.CreateQuery<TElement>() 。
MyQueryProvider.CreateQuery<TElement>() 方法返回一个数据源,通常我们还会将 expression 回传给 Queryable<T> 对象,以便其在稍候调用 MyQueryProvider.Execute<TResult>() 时作为参数。至此,我们所持有的依然是原本那个对象 o,只不过这里面多了个表达式。
(3) 当开始执行 Main() "foreach (int item in q)" 代码时,将会触发 LINQ 延时执行机制,MyQuery<T>.GetEnumerator() 方法被调用。在该方法内部我们将上一步存储的表达式传递给了 MyQueryProvider.Execute<TResult>(),同时指定了返回集合的类型。
(4) 在 MyQueryProvider.Execute<TResult>() 中,我们通过分解表达式树来创建相应的委托,最终返回经过筛选的结果。
----------------
参考文章:
《动态创建 Lambda 表达式》
《LINQ to SQL 执行流程不完整分析 (Update)》
《LINQ - 延迟执行机制分析》
《LINQ: Building an IQueryable Provider》
《Writing custom LINQ provider》、《Custom LINQ provider (Continued..)》
评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=613
在前面分析 LINQ to SQL 的文章中,我们已经明白要实现一个自定义的 LINQ provider,需要实现两个接口 —— IQueryable<T> 和 IQueryProvider。其中 IQueryable<T> 是主要的外部操作接口,被 LINQ 扩展方法(System.Linq.Queryable)调用,用于保持源数据和查询状态;IQueryProvider 则是整个算法核心,分解表达式(Expression), "计算" 并 "筛选" 出我们期望的结果集(IEnumerable<T>)。
看看两个接口在 MSDN 中的说明。
IQueryable<T>
/// <summary>
/// Provides functionality to evaluate queries against a specific data source wherein
/// the type of the data is known.
/// </summary>
public interface IQueryable<T> : IEnumerable<T>, IQueryable, IEnumerable
{
}
/// <summary>
/// Provides functionality to evaluate queries against a specific data source wherein
/// the type of the data is not specified.
/// </summary>
public interface IQueryable : IEnumerable
{
/// <summary>
/// Gets the type of the element(s) that are returned when the expression tree associated
/// with this instance of IQueryable is executed.
/// </summary>
Type ElementType { get; }
/// <summary>
/// Gets the expression tree that is associated with the instance of IQueryable.
/// </summary>
Expression Expression { get; }
/// <summary>
/// Gets the query provider that is associated with this data source.
/// </summary>
IQueryProvider Provider { get; }
}IQueryProvider
/// <summary>
/// Defines methods to create and execute queries that are described by an IQueryable object.
/// </summary>
public interface IQueryProvider
{
/// <summary>
/// Constructs an IQueryable object that can evaluate the query represented by
/// a specified expression tree.
/// </summary>
IQueryable CreateQuery(Expression expression);
/// <summary>
/// Constructs an IQueryable<(Of <T>)> object that can evaluate the query represented by
/// a specified expression tree.
/// </summary>
IQueryable<TElement> CreateQuery<TElement>(Expression expression);
/// <summary>
/// Executes the query represented by a specified expression tree.
/// </summary>
object Execute(Expression expression);
/// <summary>
/// Executes the strongly-typed query represented by a specified expression tree.
/// </summary>
TResult Execute<TResult>(Expression expression);
}首先我们创建一个实现了 IQueryable<T> 的对象,这个对象对象内部包含了一个 T[] Data 数据源。
public class MyQuery<T> : IQueryable<T>
{
internal Expression expression;
private MyQueryProvider<T> provider;
public T[] Data { get; set; }
// ------------- IEnumerator Member ---------------------
public IEnumerator<T> GetEnumerator()
{
return provider.Execute<IEnumerator<T>>(this.expression);
}
IEnumerator IEnumerable.GetEnumerator()
{
return (this as IEnumerable<T>).GetEnumerator();
}
// ------------- IQueryable<T> Member ---------------------
/// <summary>
/// 获取元素类型
/// </summary>
public Type ElementType
{
get { return typeof(T); }
}
/// <summary>
/// 获取数据源(IQueryable)常量表达式
/// </summary>
public Expression Expression
{
get { return Expression.Constant(this); }
}
/// <summary>
/// 获取 Provider 对象
/// </summary>
public IQueryProvider Provider
{
get
{
if (provider == null)
provider = new MyQueryProvider<T>(this);
return provider;
}
}
}接下来,我们编写 MyQueryProvider(LINQ Provider) 类型。为了和 MyQueryable<T> 交互,我们通过构造方法传递了对象引用。
public class MyQueryProvider<T> : IQueryProvider
{
private MyQuery<T> query;
public MyQueryProvider(MyQuery<T> query)
{
this.query = query;
}
// ------------- IQueryProvider Member ---------------------
/// <remark>
/// 构造一个可用来执行 "表达式" 计算的 IQueryable 对象。
/// </remark>
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
{
query.expression = expression;
return (IQueryable<TElement>)query;
}
/// <remark>
/// 构造一个可用来执行 "表达式" 计算的 IQueryable 对象。
/// </remark>
public IQueryable CreateQuery(Expression expression)
{
return CreateQuery<T>(expression);
}
/// <remark>
/// 执行表达式 "计算"
/// </remark>
public TResult Execute<TResult>(Expression expression)
{
// 以下代码并不完整,我只是实现了 "i > 3" 这样的表达式……
var exp = expression as MethodCallExpression;
var data = ((exp.Arguments[0] as ConstantExpression).Value as MyQuery<T>).Data;
var func = (exp.Arguments[1] as UnaryExpression).Operand as Expression<System.Func<T, bool>>;
var lambda = Expression.Lambda<Func<T, bool>>(func.Body, func.Parameters[0]);
var r = data.Where(lambda.Compile());
return (TResult)r.GetEnumerator();
}
/// <remark>
/// 执行表达式 "计算"
/// </remark>
public object Execute(Expression expression)
{
return Execute<T>(expression);
}
}最后,我们开始测试一个作坊式的 "发动机",看看效果。
class Program
{
static void Main(string[] args)
{
var o = new MyQuery<int> { Data = new[] { 1, 2, 3, 4, 5 } };
var q = from i in o where i > 3 select i;
foreach (var item in q)
{
Console.WriteLine(item);
}
}
}输出:
4
5
还行,如果我们能补全表达式计算代码,应该还是个不错的东西。
依照惯例,我们会进行反编译,并试图分析一下该演示的执行流程。
(1) 调用代码很好理解,首先构造表达式树,然后调用扩展方法 Queryable.Where()。
static void Main(string[] args)
{
MyQuery<int> o = new MyQuery<int>();
o.Data = new int[] { 1, 2, 3, 4, 5 };
ParameterExpression e = Expression.Parameter(typeof(int), "i");
BinaryExpression bin = Expression.GreaterThan(e, Expression.Constant(3, typeof(int)));
Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(bin,
new ParameterExpression[] { e });
IQueryable<int> q = Queryable.Where<int>(o, lambda);
foreach (int item in q)
{
Console.WriteLine(item);
}
}(2) 在 Queryable.Where() 中 MyQuery<T>.Provider 和 MyQuery<T>.Expression 被调用,MyQuery<T>.Expression 作为参数传递给 MyQueryProvider.CreateQuery<TElement>() 。
MyQueryProvider.CreateQuery<TElement>() 方法返回一个数据源,通常我们还会将 expression 回传给 Queryable<T> 对象,以便其在稍候调用 MyQueryProvider.Execute<TResult>() 时作为参数。至此,我们所持有的依然是原本那个对象 o,只不过这里面多了个表达式。
public static class Queryable
{
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate)
{
// ...
return source.Provider.CreateQuery<TSource>(
Expression.Call(
null,
((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }),
new Expression[] { source.Expression, Expression.Quote(predicate) }
)
);
}
}(3) 当开始执行 Main() "foreach (int item in q)" 代码时,将会触发 LINQ 延时执行机制,MyQuery<T>.GetEnumerator() 方法被调用。在该方法内部我们将上一步存储的表达式传递给了 MyQueryProvider.Execute<TResult>(),同时指定了返回集合的类型。
public IEnumerator<T> GetEnumerator()
{
return provider.Execute<IEnumerator<T>>(this.expression);
}(4) 在 MyQueryProvider.Execute<TResult>() 中,我们通过分解表达式树来创建相应的委托,最终返回经过筛选的结果。
public TResult Execute<TResult>(Expression expression)
{
// 以下代码并不完整,我只是实现了 "i > 3" 这样的表达式……
var exp = expression as MethodCallExpression;
var data = ((exp.Arguments[0] as ConstantExpression).Value as MyQuery<T>).Data;
var func = (exp.Arguments[1] as UnaryExpression).Operand as Expression<System.Func<T, bool>>;
var lambda = Expression.Lambda<Func<T, bool>>(func.Body, func.Parameters[0]);
var r = data.Where(lambda.Compile());
return (TResult)r.GetEnumerator();
}----------------
参考文章:
《动态创建 Lambda 表达式》
《LINQ to SQL 执行流程不完整分析 (Update)》
《LINQ - 延迟执行机制分析》
《LINQ: Building an IQueryable Provider》
《Writing custom LINQ provider》、《Custom LINQ provider (Continued..)》
[最后修改由 yuhen, 于 2007-10-21 20:30:06]
评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=613
这篇日志没有评论。







