浅谈数据访问层写下数据访问层这几个字恐怕现在的程序员很少知道是什么意思的他们可能知道数据实体知道EF和SqlSugar不知道数据访问层是咋回事。的确现在的ORM框架已经淡化了数据访问层的概念现在只要会创建实体类会调用EF就可以了框架一切都是做好了不用懂数据库也不用关心是什么数据库总之一套代码什么数据库都支持还为这种开发模式起了名字叫CodeFirst代码先行。作为一名老程序员我不知道软件开发模式为啥会演变到今天这个样子搞的越来越复杂越来越脱离根本。我们那时候的程序员大概范围是在2000年前后10年吧要想学会软件开发首先要学会使用数据库要学会写sql因为sql 是操作数据的根本然后要学会存储过程因为存储过程是性能优化的重要手段还要学会视图因为视图是数据关联的桥梁还有触发器是弥补功能缺陷的利器。而现在呢这些全不被提倡了这些传统的技术全成为了ORM 的绊脚石。在ORM框架里数据库的组织和处理能力全部被实体类代替了只剩下存储这一个功能了。现在的程序员不了解数据库软件系统出了问题也不知道是什么原因代码没问题问题可能出在数据上。就好比现在的程序员也不懂硬件甚至不会安装操作系统。我们那时候的程序员在很多人眼里就是修电脑的其实主业是写代码但人家不关心也不了解只知道会修电脑。现在呢不是这样了有人说这是行业的细分专业的人就得做专业的事但我觉得有些相关技能还是自己会比较好。程序员必须要懂数据库这是根本。我遇到过一个程序员他说他从来不会去写sql用ORM框架多简单不用ORM 就是一种落后他很执着我是没法反驳他的因为这是当下流行的趋势我只知道要解决软件系统的问题只了解程序代码是不够的很多深层次的问题要查数据库分析数据这才能从本质上解决问题否则很可能改了这个问题出来那个问题按下葫芦起来瓢永远改不完的问题。喜欢使用ORM框架的我总结了两个原因第一、为了跨数据库一套代码适合各种数据库第二、不用学习sql语法、视图、存储过程、触发器省去学习数据库的成本其他我想不出更合适的理由。我不喜欢这种开发模式ORM 把本该放在数据库上的精力放在了程序上那些拉姆达表达式比sql还难理解我觉得这是本末倒置认知上的倒退道在迩而求诸远事在易而求诸难。其实每种开发方式都有它的优缺点没有对错只有是否合适适合你的对你来说就是对的不适合你的对你来说就是错的不能一概而论。要解决问题就用自己最擅长的方式。我提倡的方式是不用实体类用传统的sql和DataTable这是最简单最灵活的方式也就是最传统的方式。这样既能熟悉了数据库又能灵活的编写代码。我知道我写这篇文章很多人会反对因为现在估计80%的人都在使用ORM框架开发包括以前喜欢写sql的那批老程序员。这里呢我也不想争论能解决问题就行。愿意用啥就用啥但是要做到问题到我这里结束不要说我不会这个这个不管我的事数据库我不会查每个人要对自己选择的方式负责到底。几年前我比较看中程序代码的编写规范我觉得代码应该看上去比较舒服编码风格要一致不仅仅是实现功能。有一位资深的程序员不太认同他说不管代码写的怎样只要数据对就行以数据库为准现在我觉得他说的有一定道理每个程序员的水平不一样程序写的不好可以重写但数据要对。产生数据的方式有很多数据来源不止一个有可能是手工录入的有可能是接口推送的有可能是导入的不管是那种方式都要确保数据的准确性和完整性。所以从本质上来说数据访问层使用什么方式并不是最重要的。数据准确性和完整性才是最重要的。如果你赞同上面的分析那么继续向下看如何设计简单实用的数据访问层如果不赞同那就到次为止。因为后面的设计思想可能让你更加不屑。数据访问层的设计要解决一下几个问题1、连接数据库。要支持连接多种类型的数据库方式主要是通过官方提供的数据库访问类例如SqlDataClient。2、基本的数据访问方法。执行新建insert、更新update、查询select以及调用存储过程这些基本就够了。再进一步归纳一下就是两个方法ExecuteNonQuery 和ExecuteQuery。3、执行数据操作返回的数据对象。新增和更新返回的是影响的数据行数存储过程返回的是执行是否成功尽量避免使用存储过程的返回值这些没什么好说的。需要说的是查询数据时返回数据对象。有两种对象一种是DataTable 一种是DataReaderDataTable 是比较传统的方式也是最早被广泛使用的。DataReader 是后期才有的可以看作是DataTable 的只读形式目的是提高读取的性能ORM 框架就是把DataReader 映射成实体类。这是目前被提倡的开发模式但我们不用这种方式而是继续用DataTable因为我们不使用实体类。下面我们对这三个问题展开说明具体如何实现。第一个问题如何实现连接多种类型的数据库。如在访问SqlServer 数据库的时候使用SqlDataClient(Net版本不同名字可能不一样)访问MySql 数据库的时候使用MySql.DataClient。 最简单的办法就是使用接口类定义一个接口类把所有数据访问方法都定义出来使用接口来调用方法再创建访问SqlServer 的实现类和MySql 的实现类都要实现该接口。然后最外层再做一个代理类Agent 用来确定接口要调用哪个类。程序结构如下图接口的定义如下图只显示主要的方法代理类的定义如下图只显示主要方法第二个问题操作数据库的基本方法有人说基本方法是增删改查这个没错我觉得再进一步会归纳一下是执行和获取数据。就是两个方法ExecuteNonQuery和ExecuteQuery。其他所有的操作都是围绕这两个方法也可以说都是调用这两个方法。要在每种类型的数据库访问类中实现这几个基本方法。以SqlServer 为例列出基本方法如下图只显示主要的方法第三个问题返回数据的对象。前面说了我们用DataTable而不用数据实体主要是因为它灵活。这样设计是在项目中成本最低的方案。我曾经设想过使用数据实体的场景那就是业务需求非常明确调研充分数据结构基本定型字段数量类型都很确切至少业务模型预演了几遍的也就保证功能做出来不会经常修改。这种情况我见过做过对日外包项目的都知道简直是变态的设计要求一个字段一个输入框的尺寸字数限制验证提示都要在文档里写清楚项目周期三个月的话文档要写两个半月。现实中这种情况太少了很多情况我们拿到个大概的需求就动手了一边做一边完善数据库字段大方向不会错增加个字段是常有的事。不要说这是违反软件开发规范的严格遵守软件规范是很理想的事情不知道大厂们是否能严格遵守软件开发规范ExecuteQuery包括两个方法一个是返回DataTable一个是返回DataReader严格来讲DataTable并不存在在Net的Framework或NetCore框架中有两种方式返回DataTable一种是通过DataAdapter对象的Fill方法填充DataTable一种是使用DataReader填充DataTable其实这已经足够用了。两种方法稍微有点差异以后再慢慢说真正使用的时候就知道了这里不能说多了说多容易跑题。数据库访问类的设计是重点除了提供基本方法外还要处理好数据库连接、数据库关闭开启事务、关闭事务事务回滚。这都是很重要的概念如果数据库只开不关很快连接数量就用完了。初学者很容易犯的错误一旦出现还很难排查。再有一个重要的概念是同一批数据操作尽量在一个数据库开闭中完成不要反复的开闭数据库例如修改一条商品信息先查询商品编号是否已经存在如果不存在可以保存已经存在提示重复不能保存。这个功能包括2个数据操作一个是按编号查询商品表一个是更新商品表。这两个动作要在一个数据库连接中完成。不能每个动作都要开闭数据库。在早期的数据库访问层HFBPM3.5中存在这个问题。那时候的设计考虑要支持本地调用和远程调用WCF的方式每个数据操作都可以调用远程的方法不能在一个本地连接里完成。后来的版本去掉了远程调用。提到WCF不得不跑个题因为涉及到微软的技术框架很早微软推出了三大框架WCF、WFF、WPF至今只留下了WPF其他两个在微软Core中基本消失了。微软的东西是好用起来方便功能也强大但淘汰也快太理想化的东西容易走偏有些东西出的晚生态就不好。比如这个MVC的架构我到现在没怎么使用这个架构但是这个思想我一直在用在微软asp.net的时候web界面要使用服务端控件这个不方便很快就被淘汰了随着ajax和jquery的兴起时我们在传统的aspx上使用ajax和jquery即使用了aspx的后台功能又使用了js前端框架的优势就没有使用MVC说实话微软的MVC框架出来的有点晚我们已经在做了HFbpm4.0使用的就是这种架构这种架构也类似于NetCore的WebAPI架构后台接口使用的MVC的路由机制利用反射技术动态调用方法比WebAPI还要灵活WebAPI的依赖注入也有很多不灵活的地方这里要细讲也需要很大的篇幅先不赘述了有兴趣的可以看一下hfbpm4.0的程序架构总之一句话用最简单的技术实现最基本的功能大道至简。这个题跑的有点远赶紧回到正题上开闭数据库和数据库事务这些功能要手动来完成默认的情况下每次数据操作都要开闭数据库一次。我们通过数学控制数据操作后下不要关闭数据库连接等操作完后再手动关闭。如下面的代码手动开启事务手动关闭事务如下图这些测试工作是比较复杂的为了方便测试开发了一个测试工具主要测试多个操作是否开闭了一次数据库数据库事务是否能正常回滚等。在执行数据库操作时为每次数据库连接定义一个随机数从数据库连接打开到关闭所有的数据库操作都返回这个随机数如果随机数不一样说明不是在一个数据库连接中完成的操作。如下图上面介绍的这些功能是数据库操作的基本功能有了这些就可以完成数据库的增删改查了。但是如果对一个开发平台来说这些还不够一个开发平台还需要有数据库维护的功能即元数据操作的功能比如创建表、创建字段、修改字段获取表结构等。这些功能也要在数据库访问层中实现。一个完整的数据库访问层应该包括一下功能以上是对数据访问层的一点浅浅的认识不正确的地方欢迎批评指正很期待与同行们的交流。淌过的水走过的路踩过的坑分享出来共同提高进步我的程序人生