由于一些历史原因,导致公司现有项目的数据库中存在大量中文表名,中文字段名,而且操作数据库的方式还是 SQL 语句拼接 + ADO.NET,当然操作数据库的方式一点问题都没,但是最让我不能接受的就是 SQL 语句的拼接,因为数据库中大量中文表名,中文字段名的原因,导致一打开相关代码,黑压压一片汉字,着实辣眼睛,为了解决这个问题,编写了 TQueryHelper 帮助类。
TQueryHelper 的主要作用是避免在拼接 SQL 语句中出现中文,所以我的解决思路是:中文表名,中文字段名,可以利用特性(Attribute)来与实体类的属性一一映射,查询语句通过 Lambda 表达式分块生成,然后利用 StringBuilder 进行拼接,因为是在现有项目上改进,所以 LinqToSql 和 EF 都不适合,因为 LinqToSql 和 EF 都需要连接数据库创建相应的实体类,这不符合需求,加上网上没有找到期望的类库,所以打算自己造,需要说明的是,Lambda 表达式生成 SQL 参考的这篇文章《由浅入深表达式树(二)遍历表达式树 作者:腾飞(Jesse)》,在此特别感谢作者。
上面说明的是编写 TQueryHelper 动机和过程,发本文的目的是期望自己写的帮助类能够帮助其他人,避免别人重复造轮子,另外由于本文水平有限,也期望大家能给出好的建议,让本人加以改进,还有如果有现成类库,本人就可以直接使用成熟的类库,这样也避免本人继续走弯路。
下面介绍一下 TQueryHelper 生成 SQL 语句的方式,增删改查虽然都有包括,但下面只介绍查询方面的,首先我们定义两个实体类:
class="brush:csharp;gutter:true;">[DataBaseT("文章表")] public class TopicModel { public int IdentityID { get; set; } [DataBaseT("标题")] public string Title { get; set; } [DataBaseT("类别")] public string TypeCode { get; set; } } [DataBaseT("文章类别表")] public class TopicTypeModel { public int IdentityID { get; set; } [DataBaseT("编号")] public string TypeCode { get; set; } [DataBaseT("名称")] public string TypeName { get; set; } }
单表简单查询代码如下:
string querySql = new TQueryHelper<TopicModel>().Query() .ToSql(); //生成的 SQL 语句如下: select [文章表].* from [文章表] with(nolock)
查询前 10 条数据,并且只查询 IdentityID 和 标题字段代码如下:
string querySql = new TQueryHelper<TopicModel>().Query(10) .Select(p => new { ID = p.IdentityID, p.Title }) .ToSql(); //生成的 SQL 语句如下: select top 10 [文章表].[IdentityID] as ID,[文章表].[标题] from [文章表] with(nolock)
复杂一点的单表查询,包括 Where 语句,Order By 语句,Group By 语句的代码如下:
string querySql = new TQueryHelper<TopicModel>().Query() .Select(p => new { ID = p.IdentityID, p.Title }) .Count(p => p.IdentityID, "Count") .Where(p => p.IdentityID == 1) .OrderDesc(p => p.IdentityID) .Group(p => new { p.IdentityID, p.Title }) .ToSql(); //生成的 SQL 语句如下: select [文章表].[IdentityID] as ID,[文章表].[标题],COUNT([文章表].[IdentityID]) as [Count] from [文章表] with(nolock) where [文章表].[IdentityID] = 1 group by [文章表].[IdentityID],[文章表].[标题] order by [文章表].[IdentityID] desc
下面来看看多表查询,简单双表内联连接查询代码如下:
string querySql = new TQueryHelper<TopicModel>().Query() .InnerJoin<TopicTypeModel>((p, y) => p.IdentityID == y.IdentityID) .ToSql(); //生成的 SQL 语句如下: select [文章表].* ,[文章类别表].* from [文章表] with(nolock) inner join [文章类别表] with(nolock) on [文章表].[IdentityID]=[文章类别表].[IdentityID]
复杂一点的双表内联查询代码如下:
string querySql = new TQueryHelper<TopicModel>() .Query() .Select(p => new { ID = p.IdentityID, p.Title }) .Select<TopicTypeModel>(p => new { TypeID = p.IdentityID, p.TypeCode, p.TypeName }) .InnerJoin<TopicTypeModel>((p, y) => p.TypeCode == y.TypeCode) .Where(p => p.TypeCode == "life" && p.IdentityID <= 20) .Order(p => p.IdentityID) .ToSql(); //生成的 SQL 语句如下: select [文章表].[IdentityID] as ID,[文章表].[标题],[文章类别表].[IdentityID] as TypeID,[文章类别表].[编号],[文章类别表].[名称] from [文章表] with(nolock) inner join [文章类别表] with(nolock) on [文章表].[类别]=[文章类别表].[编号] where [文章表].[类别] = 'life' and [文章表].[IdentityID] <= 20 order by [文章表].[IdentityID] asc
TQueryHelper 支持任意张表内联连接/左连接/右连接查询,TQueryHelper 也支持联合查询和子查询,简单联合查询的代码如下:
string querySql = new TQueryHelper<TopicModel>() .Query() .Union<TopicModel>( new TQueryHelper<TopicModel>().Query() ) .ToSql(); //生成的 SQL 语句如下: select [文章表].* from [文章表] with(nolock) union (select [文章表].* from [文章表] with(nolock))
复杂的联合查询代码也都依然支持,通过代码也能看到,联合查询之间互相无影响,所以逻辑可以相对复杂,子查询的处理方式跟联合查询类似,From In 子查询代码如下:
string querySql = new TQueryHelper<TopicModel>() .Query() .Select() .FromIn<TopicModel>( new TQueryHelper<TopicModel>() .Query() ) .ToSql(); //生成的 SQL 语句如下: select [文章表].* from (select [文章表].* from [文章表] with(nolock)) as [文章表]
目前只提供了 Where In 的支持,代码如下:
string querySql = new TQueryHelper<TopicModel>() .Query() .Select() .Where(p => p.IdentityID == 1) .WhereIn<TopicTypeModel>( p => p.TypeCode, new TQueryHelper<TopicTypeModel>() .Distinct() .Select(p => new { TypeCode = p.TypeCode }) ) .ToSql(); //生成的 SQL 语句如下: select [文章表].* from [文章表] with(nolock) where [文章表].[IdentityID] = 1 and [文章表].[类别] in (select distinct [文章类别表].[编号] from [文章类别表] with(nolock) )
至此,TQueryHelper 的主要功能都介绍完毕,TQueryHelper 支持 SQL 拼接的大部分逻辑实现,当然也支持生成参数(@)的 SQL 语句,这儿不一一列出,需要说明的是,虽然支持 CacheKey(就是根据 CacheKey 把查询语句缓存出来),但是还是无法避免表达式的参数传递,因为测试发现,比如下面的代码:
private void X<T>(Expression<Func<T, object>> lambda) {} for (int index = 0; index < 10000; index ++ ) { X<TopicModel>(p => new { p.IdentityID, p.Title }); }
上面的代码,X 函数里面什么也没做,但是测试之后发现竟然需要消耗大概 130 毫秒左右,这儿实在是没想出办法来优化,所以调用生成一次 SQL 之后应该根据 KEY 值及时缓存,这样下次调用可以直接从缓存中拿出已拼接好的 SQL 语句,而不再需要通过 TQueryHelper 生成 SQL 语句,最后,还是希望看过本文的朋友提出宝贵的改进意见,或者给出成熟的类库链接,本人感激不进。源码下载地址:https://pan.baidu.com/s/1jHG3mSm 密码:ytm5