ObjTracer 简介
ORM (Object Relation Map)应用已有好多年了,它给软件开发人员带来了巨大的好处,其相关产品也不少。本文要介绍的 ObjTracer 是一个相当不错的工具,它在 MVC(Model, View, Control)模式的架构中的模型层,在该层面上,它为后续的框架设计提供了支撑。
和目前比较流行的 ORM 工具相比,ObjTracer 在以下方面进行了扩展:
1. 支持抽象类和接口的操作(查询、删除、修改)
abstract class ConsumeCard : BizObj
{
public double DueAmount {……}
public double InitAmount {……}
public DateTime InitTime {……}
public virtual Guest guest {……}
}
class VipCard : ConsumeCard
{
Member member {get; set};
string cardNo {};
override Guest guest
{
get { return member;}
set { member = value;}
}
}
class CommonCard : ConsumeCard
{
Person person {get; set};
override Guest
{
get {return person;}
set {person = value;}
}
}
ConsumeCard o;
在ObjTracer 中可以非常方便的进行如下操作(不管是抽象类、接口、具体类还是被继承类,都遵行相同的原则):
² 删除对象,如删除指定对象 Delete(o),删除指定对象且该对象必须满足指定条件 Delete(o, “DueAmount > 20” ),删除满足一定条件的指定类别的对象Delete(ConsumeCard,”DueAmount > 20” AND “Guest = 张三”),此处“张三”为Guest 的实例对象,下同;
² 读取对象,如读取指定ID的对象Get(ConsumeCard, “ID = 10” ),实际上读取的是最终的具体类的对象实例;读取满足一定条件的对象列表Gets(ConsumeCard, Guest = 张三);
² 修改指定对象的全部属性Update(o);修改指定对象的部分属性,同时提供条件限制,如Update(o,“DueAmount = DueAmount + InitAmount”,Where Guest = 张三);修改满足指定条件的全部对象中的部分属性,如 Update(ConsumeCard,”DueAmount = DueAmount + 10” , WHERE ” Guest.Name = ‘李四’ AND Guest Is Member ”)。
2. 再复杂的类别结构都能很好的支持
interface IMember
{
virtual string Name {get};
string SeiralNo {get;set}
Datetime ValidFrom{}
Datetime ValidTo{}
}
class Member : Guest,IMember
{
Person person {get; set};
override string name {return Person.Name;}
}
class Company : IMember
{
string companyName {get; set};
string address {get;set};
Person contactMan{get;set}
override string name {return CompanyName;}
}
class Contract : BizObj
{
IMember member{get;set};
DateTime createTime{get;set}
string serialNo;
……
}
class ContractPrepay : BizObj
{
Contract contract{get;set}
double amount {get;set}
Employee casher {get;set}
}
class TempContract : Contract
{
……
}
class VipContract : Contract
{
……
}
可以用各种复杂条件对ContractPrepay进行操作,如 “Contract.SerialNo = ‘abc009’ OR Contract.Member = member1 OR Contract Is VipContract AND Contract.Member.ValidFrom > ‘ 2008-8-8 ’ OR Contract.Member Is Company”;
如果在 Member 的数据表中加入冗余的 Name 字段(因为 Member 的 Name 使用的是其 Person 的Name,实际情况则不一定会加入该字段,这要根据实际应用情况而定),则可以针对 IMember 使用 Name 来做条件。
如果还嫌不够复杂的话,可以继续深入,而且上面针对ContractPrepay的操作不受任何影响,照样能够完成任务。
class VipContractPrepay : ContractPrepay
{
VipContract vipContract {get;set}
new Contract Contract
{
get { return vipContract;}
set {vipContract = value;}
}
double Amount {get;set}
Employee Casher {get;set}
}
此时可以专门针对VipContractPrepay 进行任意操作。
以上所有操作均与数据库的结构无关,也就是说不管对象和数据表如何映射,所有操作都成立。
3. 针对类别属性的查询,使得编程变得更容易
class Appoint : BizObj
{
Employee employee{get;set}
Position position {get;set}
Bool isValid {get; set}
……
}
我们可以这样读取某个职位上的员工列表,GetObjs(Appoint, Employee, 条件1,条件2),其中 条件1 和条件2 分别表示任职表和员工应满足的条件,如 GetObjs(Appoint,Employee, “isValid = true AND (position = 收银员 OR position.Department.Level = 5)”, “Age > 33”) ,也可以GetObjs(Appoint,Employee, “isValid = true AND (position = 收银员 OR position.Department.Level = 5) AND Employee.Age > 33”)。
4. 相同对象的重用,减少对数据库的访问次数
在一次读取多个ContractPrepay对象时,其中的Contract和Casher可能会有相同的实例出现,此时会将他们指向相同的对象进行共用,而不是每个ContractPrepay实例都有自己独立的相关这两个属性的对象实例,如果需要进一步读取这些共用对象时,可用刷新的方法完成,如 Refresh(contractPrepay1.Casher),这样可避免重复读取相同的对象。
5. 数据库的设计更加灵活,继承关系的类别可以映射到相同或不同的表,不同类别可以映射到相同的表,被继承类(包括接口)的属性可以映射到继承类的表中,这对于提高效率非常有用。
并非所有类别都要映射到数据表中,这要根据应用系统的实际情况来定,类别和表间映射的复杂程度与代码编写量间是成反比关系的,也就是说映射关系越复杂,实现代码就越简单。而代码繁简与数据库操作效率间又是成反比关系的,数据库操作效率与映射关系的复杂程度成正比关系。
以合同预付款(ContractPrepay)为例,如果系统并不对具体合同预付款(VipContractPrepay,TmpContractPrepay)进行编程,或对它们并不关心,可以考虑不设置具体合同预付款类别的表格,或将它们与合同预付款的表格放在一起,这对减少代码量和数据库效率都有帮助。
也可以考虑只设置具体合同预付款的表格映射,不设置合同预付款表格,这样做也可行,此时最好不针对合同预付款对象进行编程,而是针对具体合同预付款进行编程,这样代码量稍微增加,但对数据库的效率更有利。
当合同预付款和具体合同预付款都映射到不同表格时,合同预付款的属性放置在哪里要看具体应用系统的实际情况作出判断,不管放在哪里,都不影响编码,但对于数据库的效率有很大影响。
到底如何映射,要根据业务的实际情况和代码量进行综合权衡。
6. 可以同时连接到多个数据源
可以同时连接到多个数据源进行操作,但彼此独立,不能交叉访问。也就是说一个对象的操作只能在一个数据源中进行处理。
7. 针对同个类别的查询、添加、修改和删除可以分别在不同的数据表(或视图)中进行,这对于数据库分布在各地的应用很有帮助。
对于集团应用系统,常常各子公司都有独立的系统,此时可以在集团系统中读取整个集团的数据,但集团进行添加、修改和删除时局限于自身的数据,这时只需在集团系统中将对象的查询映射到一个视图中便可。
8. 对重载属性的支持保证了针对顶层类进行编程原则得以很好的遵守
如要针对ConsumeCard.Guest进行编程,ObjTracer 会自动定位到VipCard.Member和CommonCard.Person 上面,当然直接对后面两种情况进行编程有利于数据库的效率。
9. 编程更贴近对象,利用复合属性可以非常简洁的解决复杂条件的操作
直接针对属性进行编程,无须考虑数据映射,代码简洁且大大减少了工作量。如 Contract.Member.ContactMan.Country ,可以延伸到任何有映射的地方。
10. 可以跨越类别进行查询和统计;
ObjTracer 提供灵活的自定义查询统计策略,也是针对类别属性进行编程的,可以根据需要生成特殊要求的查询。
11. 可以针对部分属性进行(修改、查询)
查询和修改时,可以指定属性范围,而不必牵涉到所有属性,从而提高效率。针对一些敏感数据,可能需要专门的业务逻辑去修改以便保证其安全,该功能就显得很有用。
12. 提供直接使用数据库函数和存储过程的方法
可以通过 ObjTracer 直接调用数据库中的函数和存储过程,也可在查询和条件中用数据库函数。
13. 提供对 DTO 的支持
有了对 DTO 的支持,可以大大减轻控制层的代码量,使得对象的包装更轻松。
14. 可以控制属性的读写方式
每个属性可以指定是否能够读取和写入以及写入的时机,这对一些冗余字段的设计非常有用,实际应用系统有时会出于安全考虑不允许随便改动某些属性,而是通过特别的用例才能更改他们,这时指定写的方式就很有用了。
15.