背景是有一个游戏服务器一直以来都是写SQL的, 后来改过一段时间的redis, 用的是别的员工写的类orm方式将实体类型映射成各种key-value对进行写入, 但是仍有一个缺点就是需要在增\删\改的时候显式调用API, 更糟糕的是要注明删\改的字段名, 不然就会整个实体重写入. 实际使用中经常会出现写错字段名, 或是重命名字段之后忘记修改旧的字段名字符串参数, 甚至忘记调用update API导致数据没有保存...(同样的情况也发生在写SQL的时候)

其实增/删/改用到的SQL或是reids api都非常简单, 都可以自动生成, 免去反复书写的麻烦也容易写错的问题.

于是乎, 我计划通过监视/拦截增/删/改 方法以在数据修改的同时自动生成SQL/Redis调用, 来对改属性值进行保存. 其实增删比较简单, 只要自定义一个集合类型继承下IEnumerable<T>接口, 然后实现增删方法即可, 对原代码的修改量也是极小的.

但是改就有点麻烦了, 需要拦截属性的set方法, 这是以前没有怎么用过的AOP领域.

一些调查之后, 觉得有两种方式比较适合.

一是静态织入, 就是用类似代码模板的方式, 先写实体类型模板, 然后生成真正的实体类型类库, 在生成的时候进行修改在属性的set方法中注入PropertyChangedNotify事件, 服务中使用的是生成后的实体类库. PostSharp貌似不错, 但是不想引入更多的开销就罢了. 用T4或者上Emit或许不错.

二是动态, 大致上就是生成一个代理类型, 拥有原类型的所有接口, 但是重新给实现了一遍. 可以是组合的方式, 将原类型实例作为私有成员保存, Emit生成所有原类型的开放方法; 也可以是继承的方式, 重载. 考虑到对原代码的修改量, 我觉得继承重载不错.

比如原实体类型如下:

public class Person
{public string Id{get;set;}public string Name{get;set;}
}

对这个类实现修改保存的托管, 那么直接使用这个类型是不行的了(除非方案1静态修改注入), 只有使用动态生成的代理实体类, 而如果使用组合方式的代理类型, 在OO的情况下很别扭, 所以我选择用继承重载的方式. 这就需要对这个实体类型进行改造, 将所有需要监视的属性修改成virutal声明 ctrl+H搞定.

然后, 因为是增删改, 所以Key是必不可少的, 那么实体需要用attribute标记出Key属性, 支持多属性组合Key. 因为原代码用过一段时间的entityframework, 所以这个改造非常顺利.

最后的类型定义如下:

[DataContract]
public class Person
{[Key]  public string Id{get;set;}public virtual string Name{get;set;}
}

首先用attribute标记Person为需要代理的类型, 便于在实际使用中纠错和预加载, 然后标记Id属性为Key, 并且不需要重载, 其他属性声明为virtual表示需要重载进行监视.

然后手写一个代理类型:

public class PersonProxy : Person
{public override string Name{set{base.Name= value;Interceptor.Invoke(this, "Name", value);}}
}public static class Interceptor
{public static void Invoke(object instance, string name, object value){//log
    }
}

再定义个Factory的静态类, 实现静态泛型函数Create<T>()来创建Person的代理类型PersonProxy. 其实后面调用的都是代理类型, 只是使用方法和Person一致, 兼容旧代码.

只是这样的话, 还不如使用静态织入了. 所以上面手写的这个PersonProxy需要动态生成, 这样通过泛型来支持所有自定义class, 减少频繁修改. 于是Emit登场了.

不过我对IL和Emit认识浅薄, 我的办法是使用VS自带工具 -- ildasm, 用它打开刚才生成的PersonProxy查看IL代码, 反复对比总结之后终于将这个PersonProxy类型的动态生成用Emit实现成功并修改成泛型通用. 这套特殊的IL学习方法论真是屡试不爽. 方法大致如此抛砖引玉, 所以这里也就不贴emit的代码了. Invoke织入也可以做各种进化, 以达成更高级的功能, 比如invoke在赋值前, 写入失败回退throw exception等等...

这样ProxyFactory.Create<T>()函数就写好了.

在Invoke里生成需要SQL或是redis set方法就可以了.

最后再实现增删代理的集合类型, 在Add的时候直接将插入的对象通过ProxyFactory.Create<T>()转成proxy子类对象, 为了避免Add之后继续使用传入的原对象而丢失对属性set的监控, 修改Add的声明为Add(ref T obj)强行修改引用为代理对象. 从入口处根绝, 完美.

理论Ok, 于是封装成类库.

最后放上代码地址 : https://github.com/Roytin/SmartDb

转载于:https://www.cnblogs.com/pasoraku/p/9502489.html

用AOP思想改造一个服务器的数据存储相关推荐

  1. 腾讯 监控系统服务器数据采集,日均采集1200亿数据点,腾讯千亿级服务器监控数据存储实践...

    这套架构优点很明显,设计简洁.有最新数据缓存.数据分布式存储.可横向扩展,同时完全自研,各自实现细节可控. 但同样存在一些问题: 数据节点 Cache 程序异常,会导致内存缓存数据丢失,进而丢失监控数 ...

  2. 千亿级服务器监控数据存储实践

    千亿级服务器监控数据存储实践 背景 近几年开源的大数据处理系统已经逐步发展到一个比较成熟的阶段了,各类大数据处理的场景都有了相应的解决方案,如同 mysql 在当今互联网公司中的关系数据存储广泛应用地 ...

  3. 记一次Linux服务器 误删数据的恢复操作

    今天把自己的一个服务器的数据 误操作删除了一个重要的数据表,心想,嗯,真特么的想扇自己个耳光,哈哈哈 好,事不宜迟,进入正文 事情是这样的.我执行了TRUNCATE操作,导致表清空了,那么静下心来想 ...

  4. Spring AOP思想的理解和简单实现

    Spring之Aop的简单实现 所谓Aop,即Aspect Oriented Programming,面向方面编程.这个概念听起来可能有点抽象,所以在这里我们先引入Aop中的一些术语并结合它们来对Ao ...

  5. 香港服务器能不能确保数据存储安全?

    通过著名的网络安全监视机构" Heng Security Research Institute",它似乎已经使用黑客工具暴露于后门,该黑客工具暴露了全球超过90,000台计算机.基 ...

  6. 2/3企业遭受ERP数据泄露,服务器系统数据该如何保护?

    随着各类业务系统的普及和应用范围的增广,很多关系到企业生命的机密信息都被存储在服务器系统中,这些机密的安全不容乐观.根据Onapsis的最新研究,在过去两年中,依赖SAP或Oracle的企业用户中有近 ...

  7. 华为S5300数据结构及服务器恢复数据方法

    服务器数据恢复指的是通过技术手段将原本存储在服务器.存储设备内的,由于误操作.硬件故障.恶意攻击等原因丢失的数据进行修复提取的专业技术.在介绍服务器数据恢复前我们首先需要了解服务器的数据结构.文件存储 ...

  8. windows mobile开发循序渐进(4)移动应用程序的数据存储之本地数据存储第二篇

    之所以分解来写,是因为在写的过程中会有些穿插的工作,希望能够尽量写的更细节一些. 上篇说的是本地存储的XML和DataSet之间的交互,现在我们来看看windows mobile 是如何与数据库进行交 ...

  9. node.js基础:数据存储

    无服务器的数据存储 内存存储 var http = require('http'); var count = 0; //服务器访问次数存储在内存中 http.createServer(function ...

  10. 格式化zookeeper命令_zookeeper原理篇Zookeeper的数据存储与恢复原理

    前言 经过前面的一些文章的学习和了解,我们对Zookeeper有了一定的理解. 前文直达链接: zookeeper原理篇-Zookeeper选举过程分析 zookeeper原理篇-Zookeeper会 ...

最新文章

  1. 为pony程序添加IACA标记(二)
  2. memcached全面剖析–2.理解memcached的内存存储
  3. hdu 携程全球数据中心建设 (球面距离 + 最小生成树)
  4. 自定义listView添加滑动删除功能
  5. lua代码格式化工具_FFLUA——C++嵌入Luaamp;扩展Lua利器
  6. LeetCode Construct the Rectangle
  7. 不悲观-不emo-永远保持正能量——21年末,去掉所有的不开心
  8. 阿里资深技术专家:如何快速成长为技术大牛?
  9. 数据库设计笔记——关系型数据库基础知识(三)
  10. aref无效 lisp_aref无效 lisp_Common Lisp专题4:数组
  11. CentOS报错:Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7arch=x86_64repo=osi...
  12. linux 程序运行出错的时候,如何进行调试动态库
  13. 408计算机网络考研试题,2021考研计算机(408)试题及解析——计算机网络
  14. STM32H7---高速缓存Cache(二)
  15. ROS 中文教程目录(最好不要过度依赖这种,建议养成看英文原版习惯)
  16. 请允许我像亲人一样去爱你
  17. Pytorch实现Seq2Seq(Attention)字符级机器翻译
  18. 双机热备、双机互备与 双机双工的区别
  19. 地理空间数据云下载遥感影像
  20. Msm8960(APQ8064)平台的MSM-AOSP-kitkat编译适配(5):显示、NFC、overlay

热门文章

  1. 联想万全服务器告警信息在哪里看,华为网络设备查看告警信息
  2. php中until的用法,JavaScript_jQuery中nextUntil()方法用法实例,本文实例讲述了jQuery中nextUntil( - phpStudy...
  3. weightad interval scheduling problems
  4. Mysql之数据库与sql
  5. 数乌龟(母牛,兔子....)[打表法]
  6. java框架是什么_Spring 是什么框架?
  7. TensorFlow:作用域name_scope和variable_scope
  8. python计算最大公约数函数_python如何分享解两数的最大公约数 python代码 最大公约和最小公倍数数计算?...
  9. 局部敏感哈希(Locality Sensitive Hashing,LSH)
  10. 416.分割等和子集