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

前言

上一篇介绍了扩展类库的功能简介,通过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 [email protected]", // => select * from [Person] where [email protected]
      "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 [email protected] or addrid in (select id from Address where [email protected])
      "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": "[email protected]",
      "type": "nonexecute"
    }
  }
}

Address.json

{
  "sqls": {
    "ListSec": {
      "sql": "select id from ##tname where #{WhereSec}",
      "type": "nonexecute",
      "policies": {
        "section": {
          "sqlNames": [ "WhereSec" ] //指定sql的名称(同表下的SqlName)
        }
      }
    },
    "WhereSec": {
      "sql": "[email protected]",
      "type": "nonexecute"
    }
  }
}

配置执行器调用:

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

查询缓存与缓存清理策略

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

一级查询缓存策略

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

配置:

{
  "sqls": {
    "GetListL1Cache": {
      "sql": "select * from ##tname where [email protected]",
      "type": "query",
      "policies": {
        "l1cache": {} //一级查询缓存,仅作用于SqlConfigExecutor对象
      }
    }
  }
}

配置执行器调用:

        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);
        }

二级查询缓存策略

二级查询缓存,作用于整个程序运行期间,也可配置到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();
            }
        }

更多的配置说明:

{
  "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<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     }

时间: 2024-10-15 05:40:51

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

让EFCore更疯狂些的扩展类库(一):通过json文件配置sql语句

前言 EF通过linq和各种扩展方法,再加上实体模型,编写数据库的访问代码确实是优美.舒服,但是生成的sql不尽如意.性能低下,尤其是复杂些的逻辑关系,最终大家还是会回归自然,选择能够友好执行sql语句的ORM,认认真真的编写sql:问题是:EF是否也能够很友好的执行sql语句?EF提供直接执行sql语句的方法并不多,而且也是极其简单的:那是否容易进行扩展?答案是肯定的,在DbContext下提供了Database属性就是为了执行sql用的,然后自己就通过Database下的方法属性进行了扩展(

直播APP开发,扩展类库用户、会话和第三方登录集成

直播APP开发扩展类库用户.会话和第三方登录集成,允许我将些扩展类库进行开源.原来此类库的功能只是当前开发项目中的功能,我现将其抽离成可配置使用的扩展类库,以供大家共享.此类库主要特点有:1.可以和第三方登录集成,包括:微信登录.微博登录.QQ登录2.为客户端提供了直接可以调用的登录接口3.为服务端提供了直接可以检测用户登录态的操作4.支持token落地.高效缓存和分布式的数据库存储5.展示了如何开发一个项目级的类库.包括数据库配置.翻译等温馨提示:此扩展类库还在开发完善中,但已有项目在使用,感

Entity Framework Extended Library (EF扩展类库,支持批量更新、删除、合并多个查询等)

今天乍一看,园子里居然没有关于这个类库的文章,实在是意外毕竟已经有很多介绍EF使用的文章了. E文好的可以直接看https://github.com/loresoft/EntityFramework.Extended 也可以在nuget上直接安装这个包,它的说明有点过时了,最新版本已经改用对IQueryable<T>的扩展,而不是DbSet<T>(已经标记为弃用),所以跟我一样有隔离癖好的就可以大胆使用了.下面介绍如何批量删除.更新.查询. 批量删除 本来我们需要这样删除 ? //

如果说我比别人走得更远些,那是因为我站在了巨人的肩上.

注意:本文内容全部来自各路大神不是本人原创,我会在每段转载开始注明作者,如有问题请及时联系我! 阅读体系: 1.NET学习方法 2.NET知识体系 3.NET系列笔记 4.NET源码研究 5.NET职业进阶 1.NET学习方法 1.[你必须知道的.NET]第二十回:学习方法论 http://www.cnblogs.com/anytao/archive/2008/05/28/must_net_20.html作者anytao. 2.NET知识体系 1.NET知识体系结构图http://www.cnb

激进派的贾跃亭和马斯克间谁更“疯狂”?

从社会发展大趋势看,激进的行为虽然看起来有些莽撞,抑或被当时的人各种嘲讽.看轻,谓之"疯狂",但也正是这些激进的行为在推动社会快速向前发展.除了乔老爷子一手打造iPhone让社会进入移动智能时代,如今还能称得上是激进派的,国内有贾跃亭,国外则是马斯克. 作为激进派代表,二者都有着常人难以理解的执着,也都规划着一个个常人难以想象,或者认为绝对不可能实现的梦想蓝图.当然,现在贾跃亭和马斯克的处境完全不一样--贾跃亭的乐视可谓"四面楚歌",而马斯克依然春风得意,走在激进的

Java千百问_08JDK详解(004)_jdk基础扩展类库都有什么

1.jdk基础扩展类库都有什么 了解java核心框架看这里:java核心框架是什么样的 jdk基础类库分为两大部分,基础库.扩展基础库,提供了相当大量的功能,扩展基础库具体如下: I/O工具(I/O) java.io和java.nio包提供了一组非常丰富的api来管理应用程序的I/O(输入/输出).包括文件和设备I/O.对象序列化.缓冲区管理.字符集支持等.此外,API还为服务端提供了包括多路复用.非阻塞I/O.内存映射和文件锁等功能. 了解更多java.io和java.nio包看这里:[io包

[3.11] 扩展类库:基于FastRoute的快速路由

3.11.1 扩展类库:基于FastRoute的快速路由 此扩展基于 实现,需要 PHP 5.4.0 及以上版本,可以通过配置实现自定义路由配置,从而轻松映射到PhalApi中的service接口服务. 3.11.2 安装和配置 (1)扩展包下载 从  PhalApi-Library  扩展库中下载获取 FastRoute 七牛扩展包,如使用: git clone https://git.oschina.net/dogstar/PhalApi-Library.git 然后把 FastRoute

PHPEXCEL扩展类库应用说明

云智慧(北京)科技有限公司 刘建凡 应用场景:很多时候需要把数据转成EXCEL表格形式下载下来,方便用户拿去分析.传统的EXCEL做法是直接通过header头,结合table表格,转为EXCEL文档,但是这样不够灵活,不能有多个页签,不利于后期维护和扩展. 云智慧--监控宝产品中,数据的信息量比较大,用户下载下来的EXCEL文档中,需要关心的数据以及数据的对比性不同,就需要数据按照不同形式排序,分页签,来对比,使数据一目了然.我们采用的就是用PHPEXCEL这个扩展类库来做. 效果如图:以下是一

android开发时如何让svn更快些

因为和git比起来,svn更熟悉些.就先用的svn来做版本管理,反正就几个人,没什么离线提交的需求. 参考https://oomake.com/question/984356,我有了一些实践. 如果仅仅是export,您可以使用GNU Parallel进行并行svn签出. 例- svn ls 'https://foo/bar' | parallel svn export 'https://foo/bar/'{} 这将在'bar'目录下启动并行svn签出. 要是co,和update time sv