DataQuicker2完全基于.NET 2.0的新特点,代码简洁、唯美,性能非常好(已利用VS2005的新功能Performance测试和写SQL构建的数据层做了性能对比,相差不会超过10%)。
DataQuicker2主要的特点:
1)我设计的DQL,几乎完全和SQL一致,从开发者的角度来说,和写SQL差别不大。不过是结合了实际应用中的特点加强了查询的便利性,使数据的查询变得非常简单。举例来说,通常我们在查询中有多个可选项,对于查询用户来说有用户ID、用户名、职位、创建日期范围等,不过每一项都是可选的,通常我们就需要在程序中自己来组装SQL条件,即使使用其他的ORM,也需要自己拼装Criteria,而DQL的语法则是一次性列出所有的SQL条件:
select *
from users
where
userid like '%{%UserID%}%'
and username like '%{%UserName%}%'
and position = '{%Position%}'
and (CreatedTime>='{%StartTime%}' and CreatedTime<='{%EndTime%}')
在DataQuicker2中有个DqlQuery类,可以通过它的到这条DQL语句,然后我们在程序中,比如
DqlQuery query = 创建对象;
query["UserId"] = "Eunge";
query["EndTime"] = DateTime.Now;
--生成的SQL语句--
select *
from users
where
userid like '%Eunge%'
and (CreatedTime<='2006-2-28 12:22:21')
也就是说DataQuicker2中具有词法分析,会在第一次实例化DQL中进行词法分析,之后缓存。词法分析的结果就是解离所有的条件语句,包括在From字句中视图查询中的条件,或者子查询的条件,生成一种DQL XML的中间语言(我自己命名的^_^)。然后在程序中再根据赋予的变量值{%%}来动态重新组装SQL(这种形式的为变量)。其中采用特殊的优化算法,经Performance Test证实,除了首次运行会稍慢外,之后的整个查询过程和直接写SQL可以近似相等,因为性能的瓶颈都到与数据库链接中去了,比如0.01秒和0.012秒的差别可以忽略不计。
DQL是以XML格式的文件与数据实体类同名作为嵌入资源编译到生成的实体类中。比如User.cs,那么DQL为User.dql.xml。当然,如果该实体上不需要构建查询,可以不加入这个.dql.xml,看一个完整的DQL文件定义,sellers.dql.xml:
<?xml version="1.0" encoding="utf-8" ?>
<dqls>
<dql name="GetAllSellers" requiredWhereClause="true">
<param name="SellerId" type="System.Int32"></param>
<param name="SellerName"></param>
<param name="Position" type="System.String"></param>
<param name="StartTime" type="System.DateTime"></param>
<param name="EndTime" type="System.DateTime"></param>
<text>
select *
from sellers
where
userid like '%{%SellerId%}%'
and username like '%{%SellerName%}%'
and position = '{%Position%}'
and (CreatedTime>='{%StartTime%}' and CreatedTime<='{%EndTime%}')
</text>
</dql>
<dql name="QuerySellers" requiredWhereClause="false">
<param name="Id" type="System.Int32"></param>
<text>
select * from sellers where [id]>{%Id%}
</text>
</dql>
</dqls>
从这里的DQL文件中我们可以获知,在一个DQL文件中可以定义多条DQL语句节点[DQL],Name为当前DQL文件中的唯一名称,而requiredWhereClause的作用是,在查询时,当Where条件中的所有变量在未赋值,并造成了没有WHERE条件存在时,如果requiredWhereClause为true(必需Where条件),那么整个SQL返回空字符串,造成查询出错。而设定为false时,则返回不包括Where条件的SQL语句并进行查询。这个属性的设定是为了防止当没有设定任何条件时进行查询,而查询结果很大,造成性能严重开销,所以设定为true已防止运行没有任何查询条件的SQL执行。
而<param name="Id" type="System.Int32"></param>是声明变量,在这里声明的变量才可以在下文text中使用。其中name为变量名称,不区分大小写,下文中使用的方法是{%变量Name%},而type是设定该变量的类型,应该为FullName,该属性值并非必须,如果设定了,在赋值时,系统会做类型检查,否则不会。有意思的是,如果type为实现了System.IList的类,则设定的只会以列表方式出现,比如:
<dql name="QuerySellersByIds" requiredWhereClause="true">
<param name="sellerids" type="System.Collections.ArrayList"></param>
<text>
select * from sellers where sellerid in ({%sellerids%})
</text>
</dql>
C#代码:
ArrayList list = new ArrayList();
list.Add("Eunge");
list.Add("Lucy");
DqlQuery query = 实体对象.CreateQuery("DQL名称");
query["sellerids"] = list;
那么生成的SQL语句为select * from sellers where columnName in ('Eunge', 'Lucy')。当然在这条DQL中,我们看到requiredWhereClause为true,也就是说,如果我们没有对query["sellerids"]赋值,那么在查询中DataQuicker2会报错。如果设定requiredWhereClause为false,且没有对query["sellerids"]赋值的话,则返回select * from sellers
DQL必须作为.dql.xml文件附加到实体类上,比如实体类名称是Sellers,则类文件是Sellers.cs,那么DQL语句的文件是Sellers.dql.xml,作为嵌入编译到DLL中。
2)其实在DataQuicker2中,查询的方式有三中,除了上面介绍的DQL外,DataQuicker2中有一个IHelper接口,允许直接操作SQL语句。类似于Enterprise Library中的SqlHelper,不过它是以Factory模式出现的,可以实现对多种数据库的操作,不过在目前的DataQuicker2中,我只写了一个SqlHelper实现该接口,可以执行SQL。
另外,还有一个ObjectQuery类,用于设置对象来查询,比如一段代码:
ObjectQuery query = 实体类.CreateQuery();
query.SetSelectFields(实体类.字段属性);
query.SetSelectFields(实体类.字段属性, "别名");
query.SetSelectFields(实体类.字段属性, 聚合函数enum, "别名");
query.SetAssociation(...)//以与其他的表或视图建立关联
query.SetCriteria(IColumn, Operator, params object[])//设置查询条件
这种查询方式比较常见了,不再做过多的介绍,大家看看里面的UnitTest工程下面的使用方法就知道了。
3)数据分页,DataQuicker2内置了数据分页功能,是真正的数据分页,ObjectQuery或者DqlQuery都具有这个功能。数据分页对于MSSQL2000和2005都一样有效,相关属性有PageIndex, PageSize和PagingType。
比如这段DQL:
<dql name="GetAllSellers" requiredWhereClause="true">
<param name="SellerId" type="System.Int32"></param>
<param name="SellerName"></param>
<param name="Position" type="System.String"></param>
<param name="StartTime" type="System.DateTime"></param>
<param name="EndTime" type="System.DateTime"></param>
<text>
select *
from sellers
where
userid like '%{%SellerId%}%'
and username like '%{%SellerName%}%'
and position = '{%Position%}'
and (CreatedTime>='{%StartTime%}' and CreatedTime<='{%EndTime%}')
</text>
</dql>
C#代码:
DqlQuery query = 实体.CreateQuery("GetAllSellers");
query["StartTime"] = DateTime.Parse("2000-1-1");
query["EndTime"] = DateTime.Now;
query.PagingType = PageType.ManagedPaging;
query.PageIndex = 20; //第20页
query.PageSize = 10; //其实默认为10,每页记录为10条
DataTable dt = new DataTable();
query.Fill(dt);
Assert.IsTrue(dt.Rows.Count <= 10, "第20页的记录数小于等于10");
同样,使用ObjectQuery的话.
ObjectQuery query = 实体类.CreateQuery();
query.SetSelectFields(实体类.字段属性);
query.SetSelectFields(实体类.字段属性, "别名");
query.SetSelectFields(实体类.字段属性, 聚合函数enum, "别名");
query.SetAssociation(...)//以与其他的表或视图建立关联
query.SetCriteria(IColumn, Operator, params object[])//设置查询条件
query.PagingType = PageType.ManagedPaging;
query.PageIndex = 20; //第20页
DataTable dt = new DataTable();
query.Fill(dt);
Assert.IsTrue(dt.Rows.Count <= 10, "第20页的记录数小于等于10");
随便提一下数据分页,最先我使用的是“倒-查-倒”的方法,但是后来再CodeProject上看到了一篇老外的专门写分页的文章,用了其中的一种,这对于MSSQL2000是有效的,而且现在用的这种比“倒-查-倒”要快,并且要稳定,20W数据查第100页也只需要0.1秒不到(笔记本迅驰1.4G,5400HD,1GB RAM)。大家知道MSSQL2005有了ROW_Number(),我们可以用来分页了,我本来也开始对针对MSSQL2005的Provider进行使用ROW_Number分页优化,但是发现ROW_Number分页不理想,网上介绍的很多,但我估计用的人、试验了的人并不多,恰好我的机器上有MSSQL2005。
这是我使用ROW_NUMBER分页的一段SQL,
SELECT TOP 10 *
FROM
(
SELECT [Name], [Category], [Price], ROW_NUMBER() OVER (ORDER BY Price) AS RowNo
FROM [Products]
) AS A
WHERE RowNo > 10000
但是我惊讶的发现,这段SQL执行说消耗的时间比使用老外的那个分页存储过程消耗的时间还长,大概是1:0.9,10%左右的差距。我不知道为什么,我不是炫耀那个分页存储过程,那也不是我写的。所以,后来对于SQL2005我没有再专门写Provider,和MSSQL2000的通用。
4)DataQuicker2完全基于C# 2.0写的,大家可以看到应用范式后比较流畅的架构设计,不过对于DqlQuery和ObjectQuery不是太好,我没有抽象两个查询对象倒一个基类中。
5)性能我自己还比较满意,前几天第一次发了关于DataQuicker2的文章后,有很多朋友加我讨论了,在性能上面又做了些优化,把原来实例化是使用DataTable来填充数据全部变为了DataReader,以减少一次填充的消耗。另外,那个ObjectQuery查询对象也是新增加的,为了提供一种更方便的查询途径,不过只做了一些单元测试,不能确定其中是否存在BUG,或存在多少BUG。DataQuicker2现在用于搜价网http://www.sj110.com/的正式环境中,从我目前使用到的功能来看,还算稳定,性能也不错。
6)存储过程也有实体类,可以使用代码生成器直接生成存储构成的实体类,调用非常简单。
7)附带了代码生成器,可以从表、视图和存储过程直接生成DataQuicker2实体代码。不过生成器有个小瑕疵,就是要ConnectionString必须配置2个,一个是OLE的,一个是SQL的,因为生成表和视图我用的是OLE的方法。不过取存储过程信息的那段代码是Tony Qu写的,它用的是SQL读系统表。
不过DataQuicker2的缺点
1)只支持单主键,主键值可以是数据库自增、或者由DataQuicker2托管的自增长或GUID,或者用户自己管理。
2)只支持MSSQL2000和MSSQL2005,我自己不懂Oracle、MSSQL、Infomix等,所以没法写它们的Provider数据组件,遗憾。因为是VS2005了,Access我也没有考虑了,并且我一致在忙搜索引擎的开发和推广,一个人精力实在有限。
最后注明一下,当前这个DataQuicker2版本和一些网友在MSN上向我索要的那个版本不一样,做了不少改进,无论从性能上还是功能上,比如DataTable->DataReader,增加了ObjectQuery查询等。
DataQuicker2是完全因为我在开发搜价网相关系统中需要才开发的,我并不是因为ORM或开源才开发的这个。我在这里发布完全是分享一些心得,如果不喜欢的朋友别看,可以提意见,但请注意言语,谢谢。
下载地址:https://files.cnblogs.com/lovinger2000/DataQuicker2.zip
另外,我更希望大家喜欢价格搜索引擎-搜价网,算国内目前不多的电子商务搜索引擎之一,但专业于在互联网上搜索电脑、数码和手机商品,并为用户提供商品的信息和价格查询。用户可以在这里迅速地找到互联网上最低价的电脑、电子商品。搜价网的这项服务能显著地帮助用户减少购物时消耗的商品查询和搜索时间,迅速地市场价格建立准确的定位,并且因此而节省购物开销。网址是http://www.sj110.com/
目前搜价网的第二代搜索引擎正在开发中,希望大家多多支持、多多关注、多多宣传。谢谢了。

浙公网安备 33010602011771号