ShadowSql的主要思想通过表和字段的影子来拼写sql
.net中的表达式树是作为模型类和查询逻辑的影子,非常契合ShadowSql
拿表达式树来拼写sql就和EF类似
一、nuget包
nuget安装ShadowSql.Expressions
引用命名空间: ShadowSql.Expressions
二、使用用法
1. 表达式查询
1.1 按常量查询
1
2
3
|
var
query =
new
TableSqlQuery<User>(
"Users"
)
.Where(u => u.Name ==
"张三"
);
// SELECT * FROM [Users] WHERE [Name]='张三'
|
1.2 按参数查询
1
2
3
|
var
query =
new
TableSqlQuery<User>()
.Where<UserParameter>((u, p) => u.Age > p.Age2);
// SELECT * FROM [User] WHERE [Age]>@Age2
|
注: TableSqlQuery不传参数tableName会以类名为表名
2. 表达式排序
2.1 对单个字段排序
1
2
3
4
|
var
cursor =
new
Table(
"Users"
)
.Take<User>(10)
.Asc(u => u.Id);
// SELECT TOP 10 * FROM [Users] ORDER BY [Id]
|
2.2 对多个字段排序
1
2
3
4
|
var
cursor =
new
Table(
"Users"
)
.Take<User>(10)
.Desc(u =>
new
{ u.Age, u.Id });
// SELECT TOP 10 * FROM [Users] ORDER BY [Age] DESC,[Id] DESC
|
3. 联表查询
3.1 主外键联表
1
2
3
4
|
var
query =
new
Table(
"Users"
)
.SqlJoin<User, UserRole>(
new
Table(
"UserRoles"
))
.On(u => u.Id, r => r.UserId);
// SELECT * FROM [Users] AS t1 INNER JOIN [UserRoles] AS t2 ON t1.[Id]=t2.[UserId]
|
3.2 逻辑表达式联表
1
2
3
4
|
var
query =
new
Table(
"Users"
)
.SqlJoin<User, UserRole>(
new
Table(
"UserRoles"
))
.On((u, r) => u.Id == r.UserId);
// SELECT * FROM [Users] AS t1 INNER JOIN [UserRoles] AS t2 ON t1.[Id]=t2.[UserId]
|
4. 插入
4.1 插入常量值
1
2
3
|
var
insert =
new
Table(
"Users"
)
.ToInsert(() =>
new
User { Name =
"张三"
, Age = 18 });
// INSERT INTO [Users]([Name],[Age])VALUES('张三',18)
|
4.2 插入参数
1
2
3
|
var
insert =
new
Table(
"Users"
)
.ToInsert<UserParameter, User>(p =>
new
User { Name = p.Name2, Age = p.Age2 });
// INSERT INTO [Users]([Name],[Age])VALUES(@Name2,@Age2)
|
5. 表达式删除
1
2
3
4
|
var
delete =
new
TableSqlQuery<Student>(
"Students"
)
.Where(s => s.Score < 60)
.ToDelete();
// DELETE FROM [Students] WHERE [Score]<60
|
6. 表达式更新
6.1 常量更新
1
2
3
4
|
var
update =
new
Table(
"Users"
)
.ToUpdate<User>(u => u.Id == 1)
.Set(u =>
new
User { Age = 18 });
// UPDATE [Users] SET [Age]=18 WHERE [Id]=1
|
6.2 参数化更新
1
2
3
4
5
|
var
user =
new
User { Id =1, Age = 18 };
var
update = EmptyTable.Use(
"Users"
)
.ToUpdate<User>(u => u.Id == user.Id)
.Set(u =>
new
User { Age = user.Age });
// UPDATE [Users] SET [Age]=@Age WHERE [Id]=@Id
|
6.3 原值叠加更新
1
2
3
4
|
var
update =
new
Table(
"Students"
)
.ToUpdate<Student>(u => u.Score < 60 && u.Score > 55)
.Set(u =>
new
Student { Score = u.Score + 5 });
// UPDATE [Students] SET [Score]=([Score]+5) WHERE [Score]<60 AND [Score]>55
|
7、表达式获取数据
7.1 直接获取全表
1
2
3
4
|
var
select
=
new
Table(
"Users"
)
.ToSelect<User>()
.Select(u =>
new
{ u.Id, u.Name });
// SELECT [Id],[Name] FROM [Users]
|
7.3 从查询表达式获取
1
2
3
4
|
var
select
=
new
Table(
"Users"
)
.ToSelect<User>(u => u.Status)
.Select(u => u.Id);
// SELECT [Id] FROM [Users] WHERE [Status]=1
|
7.3 从表查询获取
1
2
3
4
5
6
|
var
select
=
new
Table(
"Users"
)
.ToSqlQuery<User>()
.Where(u => u.Status)
.ToSelect()
.Select(u =>
new
{ u.Id, u.Name });
// SELECT [Id],[Name] FROM [Users] WHERE [Status]=1
|
7.4 分页获取
1
2
3
4
5
6
7
|
var
select
=
new
Table(
"Users"
)
.ToSqlQuery<User>()
.Where(u => u.Status)
.Take(10, 20)
.Desc(u => u.Id)
.ToSelect();
// SELECT * FROM [Users] WHERE [Status]=1 ORDER BY [Id] OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
|
三、实现方法
1. 解析表达式树
解析表达式树需要一些技巧,需要用到设计模式
1.1 Visitor解析CURD组件
CURD中好些地方都可以用表达式树
首先需要用到ExpressionVisitor类,这个类用到了访问者模式
定义基类VisitorBase继承ExpressionVisitor,定义可能用到的一些方法,这个地方用到了模版方法设计模式
实现SelectVisitor、UpdateVisitor、OrderByVisitor等
1.2 VisitSource表示数据源组件的能力和作用
另外单表可以CURD,联表也可以CURD,分组还可以查询,就又抽象出表达式数据源
数据源基类是VisitSourceBase,定义一些常用方法和必须的抽象方法也是模版方法设计模式
实现TableVisitor、GroupByVisitor、JoinOnVisitor等
1.3 Visitor和VisitSource组合起来解析表达式树
这样组合可以节省大量代码,这就是运用了桥接设计模式
感兴趣的同学可以去查看源代码
四、参数化sql
ShadowSql对参数化查询支持的很好,本人也是非常推荐使用参数化查询
其一、参数化查询可以防sql注入
其二、参数化查询sql可以复用,可以考虑把参数化查询的sql缓存起来,用Dapper调用sql和参数对象直接执行
欢迎大家尝试,有什么问题给我留言,我会尽力满足大家的需求
源码托管地址: https://github.com/donetsoftwork/Shadow,也欢迎大家直接查看源码。
gitee同步更新:https://gitee.com/donetsoftwork/Shadow
文档地址: https://donetsoftwork.github.io/Shadow/expression/index.html
如果大家喜欢请动动您发财的小手手帮忙点一下Star。
原创作者: xiangji 转载于: https://www.cnblogs.com/xiangji/p/18901576