目前我们所接触到的许多项目开发,大多数都应用了 ORM 技术来实现与数据库的交互,ORM 虽然有诸多好处,但是在实际工作中,特别是在大型项目开发中,容易发现 ORM 存在一些缺点,在复杂场景下,反而容易大大增加开发的复杂度及牺牲灵活度。使用 ORM 不写 SQL 而使数据库交互变得简单易行,是否能够达到预期效果,要画一个问号。
主要问题可能存在于以下几点:
1.大幅度牺牲性能。
2.虽然隐藏了数据层面的设计,但并没有从根本上降低数据访问复杂度,只是将复杂纬度从一个点(SQL,存储过程)转移到另一个点(代码),以EF为例,最终生成的代码性能与C#书写有很大关系,且难以通过成熟的数据库技术反查性能瓶颈。
3.对于复杂查询,ORM 力不从心,虽然从技术角度说实现肯定都能实现,但是代价是不值的。
有朋友认为 ORM 可以使不懂数据库的开发人员也能在开发中轻松实现与数据库的交互,但是,在大型项目中,让不懂数据库的开发人员做这块工作,Are you kidding me?
在我自己的项目开发经验中,ORM 还存在以下问题:
1.对于大型项目的开发,表示数据的实体类和数据库层面的持久化设计并非一一对应的关系,使用ORM根据数据库表生成一一对应的实体类模型,并不能完全适用,这是促使我实现自己的增强组件的重要原因之一;
2.在实体类中,需要进行其它编码工作,如额外的属性定义,附加额外的Attribute,部分功能实现和业务操作等,而使用ORM来生成实体类,生成时会覆盖现有实体类而导致项目自身的编码工作丢失;
直接使用 ADO.NET 又在很多时候过于繁琐,特别是取值赋值的过程非常冗余又麻烦,那能不能设计一种机制,既能拥有 ORM 所带来的一些便利,又不失 ADO.NET 的高性能和自由度呢?
基于这样的目标,我设计实现了升讯威ADO.NET增强组件。
升讯威ADO.NET增强组件有以下几个特点:
1.支持所有数据库原生操作(基于微软企业库的数据模块,并在最新版中集成了日志模块)
2.解除与数据库表模型一一对应的关系,由开发人员灵活指定映射关系。
3.支持直接使用SQL语句并根据查询结果在内存中动态映射。
4.支持调用存储过程并根据查询结果动态映射。
5.支持自动化的事务处理,可自动回滚。
6.支持一对多的映射关系,即一个实体类可以映射到多张表。
7.支持自动填充/补全数据实体类中的数据。
8.支持DataSet、DataTable、DataRow多种粒种的内存动态映射。
9.支持简单SQL构造器,支持自动生成简单的无模型映射的SQL语句。
10.高性能,高灵活性,高可维护性。
下面直接从代码示例中看使用效果:
现在假定有 User 表,包括四个字段:Id,Name,Age,ExtraInfo。
我们定义一个简单的 User 类。(亦可使用其它工具自动生成)。
public class User { public Guid Id { get; set; } public string Name { get; set; } public int Age { get; set; } public string ExtraInfo { get; set; } }
初始化升讯威ADO.NET增强组件核心类 DatabaseWrapper,
private DatabaseWrapper _dataBase = DatabaseUnity.Database;
一、简单数据操作
1.插入一条数据:
public void AddUser(User user) { _dataBase.Insert(user); }
升讯威ADO.NET增强组的 Insert 方法原型是:
public bool Insert(object obj)
Insert 方法会自动解析传入的对象实例,分析对象的类型名称(User)及其所包括的属性(Property),自动实现对User表及各字段的动态映射,将数据插入到表中。
2.查询数据
public List<User> GetUserList() { return _dataBase.Select<User>(); }
此处原理同上文一样,Select 方法自动解析对象类型,得到表,字段信息,实现数据的查询与填充。
3.修改数据
public void UpdateUser(User user) { _dataBase.Update(user); }
有些ORM框架,使用跟踪对象实例的变化的方式,基于特定对象提交数据,但是这种方式的开销非常大,升讯威ADO.NET增强组没有采用这种方式,而是直接根据提交的对象实例,更新数据库表。
4.删除数据
public void RemoveUser(User user) { _dataBase.Remove(user); }
需要注意的是,使用上文中的简单方式进行修改及删除操作,必须在实体类中指定主键字段: