使用ORM框架,必须迁就数据库的设计吗?
时间:2011-05-03 来源:深蓝医生
我在CSDN发表了一个帖子,发布一款强大的ORM工具--PDF.NET集成开发工具 ,有个朋友caozhy提出了非常尖锐的问题,我对他的问题做了回答,现在觉得他的问题很有深度和代表性,现在整理在这里供大家讨论。
================caozhy的问题是========================:
从技术的角度看,lz的想法是好的。
但是从商业的角度看,存在一些问题:
(1)开发者能不能得到技术支持的保证?培训谁来做?
(2)后继的维护谁来做?BUG修复?
(3)ORM的框架众多,lz的产品优势在哪里?定位简单还是功能强大?如果是简单,lz的这套语法/函数还是略显复杂。
(4)对于一款面向.NET的ORM框架,如果不兼容 IQueryable 接口是一种相当大的遗憾。这意味着,我还必须使用面向数据库架构的语法来操纵业务逻辑。
(5)支持很多数据库固然很好,但是lz如何处理数据库方言问题?对于大部分低端用户来说,能很好很简便地处理好MSSQL就很不错了。对高端用户来说,支持多数据库并不是唯一的需要,他们需要稳定、高效以及高伸缩性和可扩展性。说到底,还是定位问题。目前的Entity框架就做轻量,它无论是和IDE的整合,还是ORM以及和语言的整合,做的都很好。比如ModelFirst、CodeFirst或者根据表建模,而lz的方案看上去需要在数据库和模型代码之间定义两次,而且没有很好将数据库架构和模型分离。
(6)ORM本身的复杂性没有用过的人很难想象。lz因为既是使用者,又是开发者,所以有思维定势——如果我100%是这个框架的编写者,或者我对框架的所有实现完全掌握,我甚至会考虑使用自己的框架代替通用的ORM。但是,如果我不是框架的设计者,没有阅读过全部源代码(即使你提供代码,我有没有力量去读还是个问题),那么你假想的“轻量”、“简单”都是不存在的。简单的东西不是绝对意义上的简单,而是可以充分借鉴现有的知识以及对它的反馈有充分的把握。
(7)有没有能够说服我使用它可能并不是一个简单的例子,查询几条记录,事实上对比所有同类产品,实现这样的功能都很容易。我说几条EF的问题,不知道你的产品能否解决:
- 对于泛型实体的支持,假设我要设计一个考试系统:
class Questions<T> where T : QuestionBase { } { public List<T> Questions { get; set; } } class QuestionBase { } class SingleSelectionQuestin : QuestionBase { public string Description { get; set; } public List<string> Options { get; set; } public string Selected { get; set; } } class MultiSelectionQuestin : QuestionBase { public string Description { get; set; } public List<string> Options { get; set; } public List<string> Selected { get; set; } } class BriefAnswerQuestin : QuestionBase { public string Description { get; set; } public string Answer { get; set; } }
这种情况下,使用目前版本的Entity框架,我必须迁就数据库的设计,这就是目前ORM缺陷的原因。
- 对于多实例可扩展性的支持
比如我的数据库部署到 SQL Server Azure 上,我的程序托管在Windows Azure WebRole里面。可能我有10个WebRole,并发访问数据库,数据一致性怎么保证?
- 非常复杂的数据库关系和架构,比如多个外键,级联查询,唯一性约束,参照完整性约束。比如自定义函数和SQL类型等等
- 数据迁移问题,说实话,数据迁移是几乎所有人都关注的核心问题,而且是衡量ORM好坏的首要标准。对于一个渐进添加功能的Web程序,程序的升级,同时保证原有的数据平滑地迁移到新的数据库里面是非常重要的事情。对于Rails的ActiveRecord,就做的很好。迁移几乎自动进行,甚至还可以反向的迁移。
在闭源产品(我是说.NET)上开发,这条路很艰辛,很多很大的产品相继倒下了,lz要慎重。
===============我的回答==========================:
非常感谢 caozhy 的建议和问题,下面逐一回答如下:
(1)开发者能不能得到技术支持的保证?培训谁来做?
--对于正式用户(也就是花10块钱购买源码的用户)提供在线支持,培训资料在社区有相关的博客、论坛文章;
(2)后继的维护谁来做?BUG修复?
--由于PDF.NET框架是在实际商业产品中的应用,所以维护一直在进行,功能扩展和Bug修复一直在进行中;
(3)ORM的框架众多,lz的产品优势在哪里?定位简单还是功能强大?如果是简单,lz的这套语法/函数还是略显复杂。
--框架的主要特点是具有iBatis的SQL-MAP功能和支持.NET 2.0的面向对象方式的查询表达式OQL,定位是简单易用,在使用
SQL-MAP的时候,只需要写好SQL语句,有代码工具自动生成DAL代码;在使用OQL的时候,大部分都是单表简单的CRUD操作(
复杂的SQL语句都用SQL-MAP实现了),OQL.From(entity).Select(entity.Property,...).Where(entity.Property) 这样的语法还是很简单的,
很类似SQL的写法,如果有比较复杂的条件,可能表达式构造稍显得复杂(但复杂的SQL都用SQL-MAP去实现了),Update、Insert、Delete操作
最简单,不用多说了;
(4)对于一款面向.NET的ORM框架,如果不兼容 IQueryable 接口是一种相当大的遗憾。这意味着,我还必须使用面向数据库架构的语法来操纵业务逻辑。
--由于历史原因,框架最初定位在支持.NET2.0,IQueryable 是.NET 3.0以后才支持,目前正在考虑框架直接支持LINQ;
(5)支持很多数据库固然很好,但是lz如何处理数据库方言问题?对于大部分低端用户来说,能很好很简便地处理好MSSQL就很不错了。
--正因为有不同数据库的方言问题,所以框架使用SQL-MAP技术,将那些需要高效执行的、数据库特性的SQL单独写到配置文件中,当需要切换数据库的时候,
仅仅替换这个SQL配置文件即可(SQL-MAP配置文件);
“比如ModelFirst、CodeFirst或者根据表建模...”
--框架提供了从数据库来生成实体类的工具,但也允许你先ModelFirst、CodeFirst,我的许多示例(比如示例操作OQL的部分)都是直接创建实体类,
没有设计数据表的,如果采用手工方式,你可以自定义要持久化哪些属性以及如何持久化;
(6)ORM本身的复杂性没有用过的人很难想象...但是,如果我不是框架的设计者...那么你假想的“轻量”、“简单”都是不存在的。
不太认同你说的“不是设计者”就无法肯定框架是“轻量、简单”的这个观点,“轻量”可以从软件的文件大小、对环境、系统的依赖程度等方面来认定;
“简单”可以从实际使用过程体会出来,已经有不少用过或者看过框架的朋友肯定的说“确实简单”了,例如参看这篇文章的回复:
http://www.cnblogs.com/bluedoctor/archive/2011/04/01/2001887.html
不使用反射的实体类方案
http://www.cnblogs.com/bluedoctor/archive/2010/02/08/1665795.html
7)有没有能够说服我使用它可能并不是一个简单的例子...
--首先,框架不是个人闭门造车的产物,而是实实在在的项目应用的结果,比如最近我们做的银行基金分析系统,这样的系统复杂性和数据量自然不用怀疑的;
对于你的“对于泛型实体的支持”的问题,我想不是在泛型类本身支持实体的问题,而是QuestionBase具体实现类如何支持实体类的问题,你可以先CodeFirst,
先设计“领域模型”(我认为你给的例子不再是一个简单的实体类了,而是一个领域模型),再手工对实体类进行持久化,例如持久化SingleSelectionQuestin:
首先,建议你将 QuestionBase 定义为接口,
interface QuestionBase { public ID{get;set;} }
然后,修改单选实体类:
class SingleSelectionQuestin : QuestionBase,EntityBase { public string Description { get{return getProperty<string>("Description");} set{getProperty<string>("Description",value,50);}//假设Description 字段长度为50 } public string Tb_Options { get{return getProperty<string>("OptionList");} private set{getProperty<string>("OptionList",value,500);}//假设OptionList 字段长度为500 } public List<string> Options { //假设Options 的结果以逗号分隔的字符串的形式,存在与数据库表字段OptionList 中 get{this.Tb_Options.split(',').ToList();} set{this.Tb_Options= string.join(value.ToArray(),",");} } public string Selected { get{return getProperty<string>("Selected");} set{getProperty<string>("Selected",value,50);}//假设Selected 字段长度为50 } public int ID { get{return getProperty<int>("ID");} set{getProperty<int>("ID",value);} } public SingleSelectionQuestin() { TableName = "TB_SingleSelectionQuestin";//表名称 //IdentityName = "标识字段名"; IdentityName="ID"; //PrimaryKeys.Add("主键字段名"); PrimaryKeys.Add("ID"); } protected override void SetFieldNames() { PropertyNames = new string[] { "ID","Description","Selected","OptionList"}; } }
最后,在你需要的地方来获取QuestionBase类的实例,例如:
QuestionBase question= QuestionFactory.GetQuestionInstance();//假设有这一个问题类工厂 question.ID=100;//给问题ID赋值 EntityBase entity=question as EntityBase;//实体类基类 EntityQuery<EntityBase>.Fill(entity);//填充该实体 //下面对实体类进行其它操作 EntityQuery<EntityBase>.Instance.Update(entity);//保存修改
这段代码可以放到你需要的地方;
使用这种CodeFirst的方式,最后根据需要来持久化实体类,就不需要迁就数据库表的设计了。
(8)- 对于多实例可扩展性的支持
--并发访问数据库,数据一致性的要求,对于ORM来说是不是要求太高了些?这些应该是数据库或者专门的业务层去做的事情;
(9)- 非常复杂的数据库关系和架构,比如多个外键,级联查询,唯一性约束,参照完整性约束。比如自定义函数和SQL类型等等
--PDF.NET的实体类本着从简的原则,实体类没有引入复杂关系的概念,遇到这些复杂的查询,可以使用SQL-MAP功能,它可以将DataReader的结果读入实体类中;
(10)- 数据迁移问题,说实话,数据迁移是几乎所有人都关注的核心问题,而且是衡量ORM好坏的首要标准。
--下面这个场景是否与你的这个问题类似?
我们有一个系统,有一部分基础数据需要从我们的SQLSERVER库远程同步到客户的系统中,而客户的系统采用的数据库目前有SQLSERVER,PostgreSQL,这样的数据同步
算不算类似你说的数据迁移呢?请参见我下面的文章:
唯一不变的就是一直在变”--“数据”的华丽“变身术”
http://www.cnblogs.com/bluedoctor/archive/2011/02/23/1962218.html
在系统的实现中,有关数据的导入和导出,采用实体类很好的屏蔽了数据的差异,比如目标表和源表字段名称和数量不一致的问题。
注:有关PDF.NET数据访问框架的问题,请参看官网地址
或者我的博客相关文章。