Linq to “everything you want” 深入浅出(一) 实现IQueryable的基类
为了节省大家的时间,希望先了解Exssion Tree然后再看本系列的文章,关于这些方面的介绍,建议查看TerryLee的打造自己的LINQ Provider:Exssion Tree揭秘
前言:之所以要发出这个系列,主要是之前在发出的.NET 业务框架开发实战之八 业务层Mapping的选择策略 一文中,提到了查询对象的实现。在文章发出之后,园子里的朋友给出很多非常不错评论反馈,其中园友mywork提出了非常好的建议:提议用Exssion Tree来实现。同时Harold Shen也提出了这个建议。在博客园里 Linq Provider的文章很多,那就当给博客园里面的Linq主题多添加一点资料吧,分享一下,同时也为.NET业务框架实战系列中条件对象(查询对象只是条件对象的一种)的实现做铺路。 J.
现在有很多的linq to XXX(Linq to sql,Linq to javascript等),所以文章的标题意思大家就已经明白了。
本篇议题如下:
1. 条件对象怎样实现
2. IQueryable介绍
3. IQueryProvider介绍
4. IQueryable的基类实现
1. 条件对象怎样实现
开始的时候,条件对象的实现如下:
ICriteria condition
=
CriteriaFactory.Create(
typeof
(ProductBL).Where(
"
ProductName
"
, Operation.Equal,
"
book
"
);
采用这种方式固然灵活,但是最大的问题就是Where后面的条件:ProductName是基于字符串的,缺少编译器的智能感应,而且如果ProductBL的ProductName改变为了Name,,有些地方可能仍然采用的ProductName,而且这样的错误很难发现,只有在运行时才知道,再者Where条件的构造也显得有点复杂(和现在的linq相比)。如果换成如下的方式,可能就更好一些:
ICriteria
<
T
>
condition
=
CriteriaFactory.Create
<
T
>
(Exssion
<
Func
<
T,
bool
>>
func);
使用时
ICriteria
<
ProductBL
>
condition
=
CriteriaFactory.Create
<
ProductBL
>
(o
=>
o.ProductName
==
"
book
"
);
惟一很问题就是解析Exssion树。
而且上面的代码是在UI代码中被调用,然后,ICriteria 对象最后会被传到BLL,最后传到DAL,解析为SQL语句进行执行。
趁实现条件对象的机会,把IQueryable Provider具体的讲述一下,一举两得。
2. IQueryable介绍
我们从IQueryable接口入手,首先来看看接口的定义:
public
interface
IQueryable : IEnumerable
{
Type ElementType {
get
; }
Exssion Exssion {
get
; }
IQueryProvider Provider {
get
; }
}
接口中定义了三个只读的属性,简单的理解:ElementType表示查询返回对象的类型;Exssion就是查询条件集合(Exssion可以组成一棵树),Provider就是用来解析Exssion的对象。下面的接口是上面接口的泛型版:
public
interface
IQueryable
<
T
>
: IEnumerable
<
T
>
, IQueryable, IEnumerable
{
}
IQueryable对象的本质就是一个表达式,这个表达式体现了在linq查询时方法的调用。
怎么讲:首先在这里不得不提及System.Linq.Queryable类,这个类在实现Linq的查询中起了很大的作用,代码如下:
代码
public
static
class
Queryable
{
….
public
static
IQueryable
<
TSource
>
Where
<
TSource
>
(
this
IQueryable
<
TSource
>
source, Exssion
<
Func
<
TSource,
bool
>>
dicate);
public
static
IQueryable
<
TSource
>
Where
<
TSource
>
(
this
IQueryable
<
TSource
>
source, Exssion
<
Func
<
TSource,
int
,
bool
>>
dicate);
….
}
这个类的方法很多,而且这个类是专门用来对IQueryable添加扩展方法的。其中,当我们使用Queryable.Where的方法时在IQueryable对象上进行过滤时,Where方法在IQueryable的Exssion的表达式属性的顶层添加了一个表示方法调用的表达式节点(也是Exssion类型的),这个节点表示你已经调用了Queryable.Where方法。Queryable为IQueryable添加表达式树的节点。
所以说IQueryable对象的本质就是一个体现了其上的查询方法调用的一个记录树。这样当这个IQueryable在被IQueryProvider解析的时候,IQueryProvider就一步步的解析这颗记录树。
3. IQueryProvider介绍
正如我们上面介绍的,IQueryProvider才是真正用来解析Linq查询的:
代码
public
interface
IQueryProvider
{
IQueryable CreateQuery(Exssion exssion);
IQueryable
<
TElement
>
CreateQuery
<
TElement
>
(Exssion exssion);
object
Execute(Exssion exssion);
TResult Execute
<
TResult
>
(Exssion exssion);
}
其实这个接口说到底就只有两个方法,CreateQuery,Execute.
CreateQuery方法一看就知道是干什么:这个方法就让Provider基于传入的Exssion创建一个新的IQueryable实例,随后Provider就处理这个IQueryable实例的Exssion.
Execute就是真正的来解析表达式(exssion)的。实际就是遍历Exssion,然后一个个的处理。
下面我们就来看一个例子:
代码
public
class
Query
<
T
>
: IQueryable
<
T
>
, IQueryable, IEnumerable
<
T
>
, IEnumerable, IOrderedQueryable
<
T
>
, IOrderedQueryable {
QueryProvider provider;
Exssion exssion;
public
Query(QueryProvider provider) {
if
(provider
==
null
) {
throw
new
ArgumentNullException(
"
provider
"
);
}
this
.provider
=
provider;
this
.exssion
=
Exssion.Constant(
this
);
}
public
Query(QueryProvider provider, Exssion exssion) {
if
(provider
==
null
) {
throw
new
ArgumentNullException(
"
provider
"
);
}
if
(exssion
==
null
) {
throw
new
ArgumentNullException(
"
exssion
"
);
}
if
(
!
typeof
(IQueryable
<
T
>
).IsAssignableFrom(exssion.Type)) {
throw
new
ArgumentOutOfRangeException(
"
exssion
"
);
}
this
.provider
=
provider;
this
.exssion
=
exssion;
}
Exssion IQueryable.Exssion {
get
{
return
this
.exssion; }
}
Type IQueryable.ElementType {
get
{
return
typeof
(T); }
}
IQueryProvider IQueryable.Provider {
get
{
return
this
.provider; }
}
public
IEnumerator
<
T
>
GetEnumerator() {
return
((IEnumerable
<
T
>
)
this
.provider.Execute(
this
.exssion)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return
((IEnumerable)
this
.provider.Execute(
this
.exssion)).GetEnumerator();
}
public
override
string
ToString() {
return
this
.provider.GetQueryText(
this
.exssion);
}
}
上面的代码看起来有点多,有点吓人,其实上面的代码就只是简单的实现了那些接口。而且我们之前也说过:实现IQueryable的对象基本不做什么事情,而是把事情都交给Provider来处理,而Provider处理过程也是很简单的:遍历Exssion,调用Execute方法解析:
代码
public
IEnumerator
<
T
>
GetEnumerator() {
return
((IEnumerable
<
T
>
)
this
.provider.Execute(
this
.exssion)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return
((IEnumerable)
this
.provider.Execute(
this
.exssion)).GetEnumerator();
}
所以在实现的过程中,最重要的方法就是Provider的Execute。下面看看一个IQueryProvider实现的例子:
代码
public
abstract
class
QueryProvider : IQueryProvider {
protected
QueryProvider() {
}
//
正如之前所说的:CreateQuery方法就是根据传入的exssion,创建一个新的Query对象。
IQueryable
<
S
>
IQueryProvider.CreateQuery
<
S
>
(Exssion exssion) {
return
new
Query
<
S
>
(
this
, exssion);
}
IQueryable IQueryProvider.CreateQuery(Exssion exssion) {
Type elementType
=
TypeSystem.GetElementType(exssion.Type);
try
{
return
(IQueryable)Activator.CreateInstance(
typeof
(Query
<>
).MakeGenericType(elementType),
new
object
[] {
this
, exssion });
}
catch
(TargetInvocationException tie) {
throw
tie.InnerException;
}
}
S IQueryProvider.Execute
<
S
>
(Exssion exssion) {
return
(S)
this
.Execute(exssion);
}
object
IQueryProvider.Execute(Exssion exssion) {
return
this
.Execute(exssion);
}
public
abstract
string
GetQueryText(Exssion exssion);
public
abstract
object
Execute(Exssion exssion);
}
以上只是给出了一个基本实现IQueryProvider的抽象类,具体实现,我们后面讲解。
另外上面还有一个辅助类的实现:(感兴趣的可以看看,代码有点多,实际做的事情就是返回exssion中指定的类型)。
代码
internal
static
class
TypeSystem {
internal
static
Type GetElementType(Type seqType) {
Type ienum
=
FindIEnumerable(seqType);
if
(ienum
==
null
)
return
seqType;
return
ienum.GetGenericArguments()[
];
}
private
static
Type FindIEnumerable(Type seqType) {
if
(seqType
==
null
||
seqType
==
typeof
(
string
))
return
null
;
if
(seqType.IsArray)
return
typeof
(IEnumerable
<>
).MakeGenericType(seqType.GetElementType());
if
(seqType.IsGenericType) {
foreach
(Type arg
in
seqType.GetGenericArguments()) {
Type ienum
=
typeof
(IEnumerable
<>
).MakeGenericType(arg);
if
(ienum.IsAssignableFrom(seqType)) {
return
ienum;
}
}
}
Type[] ifaces
=
seqType.GetInterfaces();
if
(ifaces
!=
null
&&
ifaces.Length
>
) {
foreach
(Type iface
in
ifaces) {
Type ienum
=
FindIEnumerable(iface);
if
(ienum
!=
null
)
return
ienum;
}
}
if
(seqType.BaseType
!=
null
&&
seqType.BaseType
!=
typeof
(
object
)) {
return
FindIEnumerable(seqType.BaseType);
}
return
null
;
}
}
今天就暂时写到这里,下篇接着写。
谢谢大家!祝大家端午节快乐!呵呵
版权为小洋和博客园所有,转载请标明出处给作者。
http://www.cnblogs.com/yanyangtian
本系列文章参考: The Wayward WebLog.
原文链接: http://www.cnblogs.com/yanyangtian/archive/2010/06/13/1757526.html