让EFCore更疯狂些的扩展类库(二):查询缓存、分部sql、表名替换、遍历的策略配置

来源:互联网 发布:wpf 实时显示数据 编辑:程序博客网 时间:2024/06/05 05:59

前言

上一篇介绍了扩展类库的功能简介,通过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配置有改动

    1.  配置策略对象的初始化、替换表名、合并分部sql等的策略执行器

  b) sql执行前的策略执行器:一般用于对SqlParameter进行解析到sql中

    1.  foreach策略执行器:对SqlParameter或者某些数据类型(list/dictionary/model)进行遍历生成字串替换到sql中

  c) sql执行时的策略执行器:一般用于缓存和日志记录

    1.  sql与参数的日志记录策略执行器
    2.  查询缓存与清理策略执行器

 

表名替换策略

通过特定的标签代替表名在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 IReadOnlyList<Person> GetList() 2         { 3             tinfo = db.GetConfigTable<Person>(); 4             return tinfo.GetExecutor().QueryUseModel<Person>(new 5             { 6                 name = "tom" 7             }, null, null, 8             //通过参数传递策略对象(一般用于设置 sql执行前 或 执行时的策略对象, 9             //      而初始化型的一般在配置文件或全局策略中设置)10             new[] { new SqlL2QueryCachePolicy() });11         }

说明:

策略对象获取的优先级(如果策略对象设置到多个地方了): 执行器形参传递的策略对象 >  sql配置的策略对象 >  表配置的策略对象 >  全局策略中配置

 

分部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"    }  }}
View Code

配置执行器调用:

1         public IReadOnlyList<Person> GetListSection()2         {3             tinfo = db.GetConfigTable<Person>();4             return tinfo.GetExecutor().QueryUseModel<Person>(new5             {6                 name = name,7                 addrid = 123,8             });9         }
View Code

 

查询缓存与缓存清理策略

查询缓存分为 一级缓存 和 二级缓存。

一级查询缓存策略

一级缓存仅作用于SqlConfigExecutor对象中,而且不能设置缓存过期时间。

配置:

{  "sqls": {    "GetListL1Cache": {      "sql": "select * from ##tname where name=@name",      "type": "query",      "policies": {        "l1cache": {} //一级查询缓存,仅作用于SqlConfigExecutor对象      }    }  }}
View Code

配置执行器调用:

        public void GetListL1Cache()        {            var tinfo = db.GetConfigTable<Person>();            var exc = tinfo.GetExecutor();            var q1 = exc.QueryUseModel<Person>(new { name = _name });            var q2 = exc.QueryUseModel<Person>(new { name = _name });            //一级缓存作用于SqlConfigExecutor,那么同一个SqlConfigExecutor对象下,            //  相同的sql和SqlParameter获取的数据是一样的            Assert.True(q1 == q2);            var q3 = exc.QueryUseModel<Person>(new { name = _name + "1" });            //参数变了            Assert.True(q1 != q3);        }
View Code

二级查询缓存策略

二级查询缓存,作用于整个程序运行期间,也可配置到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<Person>();            if (span.HasValue)            {                return (int)tinfo.GetExecutor().ScalarUseModel(null, null,                    //通过形参传递缓存策略对象:更改为不自动更新时间的                    new SqlL2QueryCachePolicy                    {                        Expiry = new QueryCacheExpiryPolicy(span.Value, false)                    });            }            else            {                return (int)tinfo.GetExecutor().Scalar();            }        }
View Code

更多的配置说明:

{  "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访问更新过期时间)          }        }      }    }  }}
View Code

二级缓存清理策略

配置:

{  "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 //是否清理 所在表下 的所有缓存        }      }    }  }}
View Code

配置执行器调用:

 1     public class PersonBLL 2     { 3         DBConfigTable tinfo; 4         public PersonBLL(DbContext db) 5         { 6             tinfo = db.GetConfigTable<Person>(); 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     }
View Code

 

foreach(遍历sql参数)的策略配置

对SqlParameter的遍历配置:

{  "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<Person>(); 5             return tinfo.GetExecutor().NonQueryUseModel(new 6             { 7                 name = name, 8                 birthday = DateTime.Now, 9                 addrid = 123,10             });11         }
View Code

更详细的配置说明:

{  "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之间的分隔符        }      }    }  }}
View Code

配置执行器调用:

 1         public int AddPersonEachP() 2         { 3             DbContext db = new MSSqlDBContext(); 4             var tinfo = db.GetConfigTable<Person>(); 5             return tinfo.GetExecutor().NonQueryUseModel(new 6             { 7                 name = name, 8                 addrid = 123, 9                 birthday = DateTime.Now,10             }, null);11         }
View Code

对SqlParameter中的Model对象遍历配置:

{  "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(new10             {11                 datas = model    //设置一个Model对象到SqlParameter参数中12             });13         }
View Code

更详细的配置说明:

{  "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之间的分隔符            }          }        }      }    }  }}
View Code

配置执行器调用:

 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(new17             {18                 setdatas = new19                 {20                     birthday = DateTime.Now21                 },22                 wheredatas = new23                 {24                     name = "tom",25                     id = 12326                 },27             });28         }
View Code

 

0 0
原创粉丝点击