上一篇介绍了扩展类库的功能简介,通过json文件配置sql语句 和 sql语句的直接执行,这篇开始说明sql配置的策略模块:策略管理器与各种策略的配置。
类库源码:github:https://github.com/skigs/EFCoreExtend
引用类库:nuget:https://www.nuget.org/packages/EFCoreExtend/
PM> Install-Package EFCoreExtend
用于管理策略 与 策略执行器和调用(目前分为三种策略执行器),目的为了让配置的sql语句更加简单、自动化等等。
1) 策略类型管理
a) 管理各种策略类型,用于初始化配置文件中的策略配置转换成对象
2) 策略对象配置
a) 通过 sql配置的执行器 的形参传递策略对象
b) 通过 配置文件(分为表和sql) 配置策略对象
c) 配置到 全局策略 中(策略管理器中)
d) 策略对象获取的优先级:通过执行器的形参传递的策略对象 > sql配置的策略对象 > 表配置的策略对象 > 全局策略中配置
3) 策略执行器(一般通过策略对象进行相应的处理)
a) 初始化型的策略执行器:这种类型的会在第一次调用GetExecutor的时候执行,只会执行一次,除非sql配置有改动
b) sql执行前的策略执行器:一般用于对SqlParameter进行解析到sql中
c) sql执行时的策略执行器:一般用于缓存和日志记录
通过特定的标签代替表名在sql配置中呈现(该策略对象默认已经添加到全局策略中,因此并不一定要在配置文件中配置):
{ "policies": { //表配置的策略对象(会包含到表下的所有sql配置中) //表名策略 "tname": { //"tag": "##tname" //默认值为 ##tname } }, "sqls": { "GetList": { "sql": "select * from ##tableName where name=@name", // => select * from [Person] where name=@name "type": "query", "policies": { //sql配置的策略对象 //表名策略 "tname": { "tag": "##tableName", //默认值为 ##tname "prefix": "[", //前缀 "suffix": "]" //后缀 } } } } }
配置初始化:
1 public static void Init() 2 { 3 //加载配置 4 EFHelper.Services.SqlConfigMgr.Config.LoadDirectory(Directory.GetCurrentDirectory() + "/Datas"); 5 6 //设置到全局策略中(一般用于设置 初始化型的策略对象),将策略对象设置到全局之后,会包含到所有配置中的 7 EFHelper.Services.SqlConfigMgr.PolicyMgr.SetGlobalPolicy( 8 //TableNamePolicy对象默认已经添加到全局策略中 9 new TableNamePolicy //表名策略在配置文件中呈现的key:tname(可以通过SqlConfigConst.TableNamePolicyName获取) 10 { 11 Tag = "##tname", 12 }); 13 }
配置执行器调用:
1 public IReadOnlyListGetList() 2 { 3 tinfo = db.GetConfigTable (); 4 return tinfo.GetExecutor().QueryUseModel (new 5 { 6 name = "tom" 7 }, null, null, 8 //通过参数传递策略对象(一般用于设置 sql执行前 或 执行时的策略对象, 9 // 而初始化型的一般在配置文件或全局策略中设置) 10 new[] { new SqlL2QueryCachePolicy() }); 11 }
说明:
策略对象获取的优先级(如果策略对象设置到多个地方了): 执行器形参传递的策略对象 > sql配置的策略对象 > 表配置的策略对象 > 全局策略中配置
分部sql目的为了将sql分部在不同的配置中,以便sql的可重用:
Person.json配置:
{ "sqls": { "GetListSection": { "sql": "select * from ##tname where #{WhereSec}", //最终生成的sql:select * from Person where name=@name or addrid in (select id from Address where id=@addrid) "type": "query", "policies": { //分部sql策略,以便将sql分部在不同的配置中(注意:分部策略是对sql的分部,可以在分部sql下再进行分部sql(子分部),但是不会继承分部sql中的policies(策略对象)等的配置) "section": { //"tagPrefix": "#{", //策略前缀标记符,默认为 #{ //"tagSuffix": "}", //策略后缀标记符,默认为 } "sqlNames": [ "WhereSec" ] //指定sql的名称(同表下的SqlName) //"tableSqlNames": { //指定其他表的sql名称(key为TableName,value为SqlName) //} } } }, "WhereSec": { "sql": " #{WhereSec1} or addrid in (#{Address.ListSec}) ", "type": "nonexecute", //不用于执行的sql类型 "policies": { "section": { "sqlNames": [ "WhereSec1" ], "tableSqlNames": { //指定其他表的sql名称(key为TableName,value为SqlName) "Address": "ListSec" } } } }, "WhereSec1": { "sql": "name=@name", "type": "nonexecute" } } }
Address.json:
{ "sqls": { "ListSec": { "sql": "select id from ##tname where #{WhereSec}", "type": "nonexecute", "policies": { "section": { "sqlNames": [ "WhereSec" ] //指定sql的名称(同表下的SqlName) } } }, "WhereSec": { "sql": "id=@addrid", "type": "nonexecute" } } }
配置执行器调用:
1 public IReadOnlyListGetListSection() 2 { 3 tinfo = db.GetConfigTable (); 4 return tinfo.GetExecutor().QueryUseModel (new 5 { 6 name = name, 7 addrid = 123, 8 }); 9 }
查询缓存分为 一级缓存 和 二级缓存。
一级缓存仅作用于SqlConfigExecutor对象中,而且不能设置缓存过期时间。
配置:
{ "sqls": { "GetListL1Cache": { "sql": "select * from ##tname where name=@name", "type": "query", "policies": { "l1cache": {} //一级查询缓存,仅作用于SqlConfigExecutor对象 } } } }
配置执行器调用:
public void GetListL1Cache() { var tinfo = db.GetConfigTable(); var exc = tinfo.GetExecutor(); var q1 = exc.QueryUseModel (new { name = _name }); var q2 = exc.QueryUseModel (new { name = _name }); //一级缓存作用于SqlConfigExecutor,那么同一个SqlConfigExecutor对象下, // 相同的sql和SqlParameter获取的数据是一样的 Assert.True(q1 == q2); var q3 = exc.QueryUseModel (new { name = _name + "1" }); //参数变了 Assert.True(q1 != q3); }
二级查询缓存,作用于整个程序运行期间,也可配置到Redis中,如果同时配置了二级缓存和一级缓存默认是使用二级缓存(策略执行器的优先级有关)。
配置:
{ "name": "Person", "policies": { //二级查询缓存,作用于整个程序运行期间 / 或者跨进程分布式(Redis),如果同时配置了二级缓存和一级缓存默认时使用二级缓存(策略执行器的优先级有关) "l2cache": { //"type":"", //缓存的类型(Query默认为:query, Scalar默认为:scalar) "expiry": { //注意如果没有设置expiry的date/span,那么缓存不会过期的 //"date": "2018-01-01", //指定缓存的过期日期 //"span": "00:00:03" //指定缓存的过期间隔(换算日期为:当前时间 + 时间间隔) //"isUpdateEach": true //是否每次获取缓存之后更新过期时间(这个属性 + span属性来进行模拟session访问更新过期时间) } } }, "sqls": { "CountL2Cache": { "sql": "select count(*) from ##tname", "type": "scalar", "policies": { "l2cache": { "expiry": { "span": "00:00:03", //指定缓存的过期间隔,这里设置为3秒 "isUpdateEach": true //每次获取缓存之后更新过期时间 } } } } } }
配置执行器调用:
public int CountL2Cache(TimeSpan? span = null) { var tinfo = db.GetConfigTable(); if (span.HasValue) { return (int)tinfo.GetExecutor().ScalarUseModel(null, null, //通过形参传递缓存策略对象:更改为不自动更新时间的 new SqlL2QueryCachePolicy { Expiry = new QueryCacheExpiryPolicy(span.Value, false) }); } else { return (int)tinfo.GetExecutor().Scalar(); } }
更多的配置说明:
{ "name": "Person", "sqls": { "GetListL2Cache": { "sql": "select * from ##tname", "type": "query", "policies": { "l2cache": { //二级查询缓存,不设置缓存过期时间(缓存不过期) } } }, "GetListL2Cache1": { "sql": "select * from ##tname", "type": "query", "policies": { "l2cache": { "type": "query1" //指定CacheType,默认为query } } }, "GetListL2Cache2": { "sql": "select * from ##tname where 2=2", "type": "query", "policies": { "l2cache": { "expiry": { "date": "2018-01-01" //指定缓存的过期日期 } } } }, "GetListL2Cache3": { "sql": "select * from ##tname where 3=3", "type": "query", "policies": { "l2cache": { "expiry": { "span": "00:00:03" //指定缓存的过期间隔(换算日期为:当前时间 + 时间间隔),为了方便测试因此这里指定了3秒 } } } }, "CountL2Cache": { "sql": "select count(*) from ##tname", "type": "scalar", "policies": { "l2cache": { "expiry": { "span": "00:00:03", //指定缓存的过期间隔(换算日期为:当前时间 + 时间间隔),为了方便测试因此这里指定了3秒 "isUpdateEach": true //是否每次获取缓存之后更新过期时间(这个属性 + span属性来进行模拟session访问更新过期时间) } } } } } }
配置:
{ "policies": { //二级查询缓存清理策略 "clear": { "isAsync": true,//是否异步进行清理 "isSelfAll": true, //是否清理 所在表下 的所有缓存 "tables": [ "Address" ], //需要进行缓存清理的表的名称(一般用于清理 其他表下 的所有查询缓存) "cacheTypes": [ "query", "scalar1" ], //需要进行缓存清理的类型(用于清理 所在表下 的CacheType查询缓存) "tableCacheTypes": { //需要进行缓存清理的类型(key为TableName,value为CacheType,一般用于清理 其他表下 的CacheType) "Address": "query" } } }, "sqls": { "CountL2Cache": { "sql": "select count(*) from ##tname", "type": "scalar", "policies": { "l2cache": { "type": "scalar1", //缓存的类型(Query默认为:query, Scalar默认为:scalar) "expiry": { "span": "00:00:03" } } } }, "AddPersonL2Cache": { "sql": "insert into ##tname(name, birthday, addrid) values('tom_t2cache', '2018-1-1', 123) ", "type": "nonquery", "policies": { "clear": { "isSelfAll": true //是否清理 所在表下 的所有缓存 } } } } }
配置执行器调用:
1 public class PersonBLL 2 { 3 DBConfigTable tinfo; 4 public PersonBLL(DbContext db) 5 { 6 tinfo = db.GetConfigTable(); 7 } 8 9 public int CountL2Cache() 10 { 11 return (int)tinfo.GetExecutor().Scalar(); 12 } 13 14 public int AddPersonL2Cache() 15 { 16 return tinfo.GetExecutor().NonQuery(); 17 } 18 }
{ "name": "Person", "sqls": { "UpdatePersonEachP": { "sql": "update ##tname set $$params where name=@name", //最终生成的sql:update Person set birthday=@_ep_birthday_0,addrid=@_ep_addrid_1 where name=@name "type": "nonquery", "policies": { "eachParams": { "ignoreParams": [ "name" ], //不需要进行遍历的SqlParameter "separator": ",", //SqlParameter与SqlParameter之间的分隔符 "kvSeparator": "=" //key-value之间的分隔符 } } } } }
配置执行器调用:
1 public int UpdatePersonEachP() 2 { 3 DbContext db = new MSSqlDBContext(); 4 var tinfo = db.GetConfigTable(); 5 return tinfo.GetExecutor().NonQueryUseModel(new 6 { 7 name = name, 8 birthday = DateTime.Now, 9 addrid = 123, 10 }); 11 }
更详细的配置说明:
{ "name": "Person", "sqls": { "AddPersonEachP": { "sql": "insert into ##tname($$params.keys) values($$params.vals)", //最终生成的sql:insert into Person(name,birthday,addrid) values(@_ep_name_0,@_ep_birthday_1,@_ep_addrid_2) //"type": "nonquery", "policies": { "eachParams": { //"tag": "$$params", //默认为 $$params //"isToSqlParam": true, //默认为 true "ignoreParams": [ "id" ], //不需要进行遍历的SqlParameter //"separator": "", //SqlParameter与SqlParameter之间的分隔符 //"kvSeparator": "", //key-value之间的分隔符 "kprefix": "[", //SqlParameter Name(key)的前缀 "ksuffix": "]", //SqlParameter Name(key)的后缀 //"vprefix": "", //SqlParameter Value的前缀 //"vsuffix": "", //SqlParameter Value的后缀 "isKVSplit": true, //是否key-value分开(keys 和 values在不同地方单独生成字串) "kseparator": ",", //使用iskvSplit的时候,key-key之间的分隔符 "vseparator": "," //使用iskvSplit的时候,value-value之间的分隔符 } } } } }
配置执行器调用:
1 public int AddPersonEachP() 2 { 3 DbContext db = new MSSqlDBContext(); 4 var tinfo = db.GetConfigTable(); 5 return tinfo.GetExecutor().NonQueryUseModel(new 6 { 7 name = name, 8 addrid = 123, 9 birthday = DateTime.Now, 10 }, null); 11 }
{ "name": "Person", "sqls": { "AddPersonEachM": { "sql": "insert into ##tname(${datas.keys}) values(${datas.vals})", //最终生成的sql:insert into Person(name,birthday,addrid) values(@_em_datas_0_0,@_em_datas_0_1,@_em_datas_0_2) "type": "nonquery", "policies": { "eachModel": { "defInfo": { "iskvSplit": true, //是否key-value分开(keys 和 values在不同地方单独生成字串) "kseparator": ",", //使用iskvSplit的时候,key-key之间的分隔符 "vseparator": "," //使用iskvSplit的时候,value-value之间的分隔符 }, "infos": { //指定哪些SqlParameter需要进行遍历的配置信息(key为SqlParameter名称) "datas": null //使用默认配置:defInfo } } } } } }
配置执行器调用:
1 public int AddPersonEachM() 2 { 3 var model = new Person 4 { 5 name = name, 6 addrid = 345, 7 birthday = null, 8 }; 9 return tinfo.GetExecutor().NonQueryUseModel(new 10 { 11 datas = model //设置一个Model对象到SqlParameter参数中 12 }); 13 }
更详细的配置说明:
{ "name": "Person", "sqls": { "UpdatePersonEachM": { "sql": "update ##tname set ${setdatas} where name=@name", //最终生成的sql:update Person set addrid=@_em_setdatas_0_0,birthday=@_em_setdatas_0_1 where name=@name "type": "nonquery", "policies": { //对SqlParameter的值类型为Model(属性类)的遍历 "eachModel": { //"isAll": true, //ForeachModel是没有isAll属性的,一定要在infos指定 "infos": { //指定哪些SqlParameter需要进行遍历的配置信息(key为SqlParameter名称) "setdatas": null //指定为null,那么使用默认的配置:defInfo }, "defInfo": { //默认的配置信息(如果 Infos属性中的key没有设置(为null),那么会默认使用这个配置信息;如果isAll为true,而且没有指定infos,那么所有匹配的类型都使用这个配置) //"tagPrefix": "${", //策略前缀标记符,默认为 ${ //"tagSuffix": "}", //策略后缀标记符,默认为 } //"isToSqlParam": true, //是否将遍历获取到的值(value)转换成SqlParameter(value => SqlParameter(@param, value) ),默认为true "separator": ",", //pair-pair之间的分隔符 //"kprefix": "[", //key(属性)的前缀 //"ksuffix": "]", //key(属性)的后缀 //"vprefix": "", //value的前缀 //"vsuffix": "", //value的后缀 "kvSeparator": "=" //key-value之间的分隔符 //"iskvSplit": true, //是否key-value分开(keys 和 values在不同地方单独生成字串) //"kseparator": "", //使用iskvSplit的时候,key-key之间的分隔符 //"vseparator": "", //使用iskvSplit的时候,value-value之间的分隔符 //"ignoreKeys": [] //不需要进行操作的key } } } }, "UpdatePersonEachM1": { "sql": "update ##tname set ${setdatas} where ${wheredatas}", //最终生成的sql:update Person set birthday='2017-01-20 17:16:08' where name=@_em_wheredatas_1_0 or id=@_em_wheredatas_1_1 "type": "nonquery", "policies": { "eachModel": { "infos": { //指定哪些SqlParameter需要进行遍历的配置信息(key为SqlParameter名称) "setdatas": { "isToSqlParam": false, //是否将遍历获取到的值(value)转换成SqlParameter(value => SqlParameter(@param, value) ),默认为true "separator": ",", //pair-pair之间的分隔符 //"kprefix": "[", //key(属性)的前缀 //"ksuffix": "]", //key(属性)的后缀 "vprefix": "'", //value的前缀 "vsuffix": "'", //value的后缀 "kvSeparator": "=" //key-value之间的分隔符 }, "wheredatas": { "separator": " or ", //pair-pair之间的分隔符 "kvSeparator": "=" //key-value之间的分隔符 } } } } } } }
配置执行器调用:
1 public int UpdatePersonEachM() 2 { 3 return tinfo.GetExecutor().NonQueryUseModel(new 4 { 5 name = "tom", 6 setdatas = new 7 { 8 addrid = 123, 9 birthday = DateTime.Now, 10 }, 11 }); 12 } 13 14 public int UpdatePersonEachM1() 15 { 16 return tinfo.GetExecutor().NonQueryUseModel(new 17 { 18 setdatas = new 19 { 20 birthday = DateTime.Now 21 }, 22 wheredatas = new 23 { 24 name = "tom", 25 id = 123 26 }, 27 }); 28 }