本申请是国际申请号为PCT/US2006/025390,国际申请日为2006年6月29日,进入中国国家阶段的申请号为“200680027771.3”,发明名称为“代码生成模式”的发明专利申请的分案申请。
具体实施方式
现在参考附图描述所要求保护的主题的各方面,在全部附图中,同样的参考标号指的是相同或相应的元素。然而,应该理解,附图及其涉及的详细描述不旨在将所要求保护的主题限制于所公开的具体形式。相反,意图是覆盖落入所要求保护的主题的精神和范围内的所有修改、等效方式和替换方式。
如此处所使用的,术语“组件”和“系统”等指的是计算机相关的实体,或者是硬件、硬件和软件的组合、软件或者是执行中的软件。例如,组件可以是,但不限于,运行在处理器上的进程、处理器、对象、可执行代码、执行的线程、程序和/或计算机。作为说明,运行在计算机上的应用程序和计算机本身均可以是组件。一个或多个组件可以驻留在进程和/或执行中的线程内,且组件可以位于一台计算机上和/或分布在两台或多台计算机之间。
词语“示例性”此处被用来意味着用作示例、实例或说明。此处描述为“示例性”的任何方面和设计不必解释为较其它方面或设计更优选和有利。另外,尽管所提供的示例利用了C#程序设计语言,但可使用众多替换的程序设计语言。
此外,所公开的主题可被实现为系统、方法、装置或制品,它们使用标准程序设计和/或工程技术来生产软件、固件、硬件或其任何组合,以控制基于计算机或处理器的设备来实现此处详细描述的各方面。如此处所用的术语“制品”(或“计算机程序产品”)旨在包含可从任何计算机可读设备、载波或介质访问的计算机程序。例如,计算机可读介质可包括,但不限于,磁存储设备(例如,硬盘、软盘、磁条…)、光盘(例如,紧致盘(CD)、数字多功能盘(DVD)…)、智能卡和闪存设备(例如,卡、棒)。另外,应该理解,可使用载波来携带计算机可读电子数据,诸如在发送和接收电子邮件或访问诸如因特网或局域网(LAN)等网络中所使用的那些。当然,本领域的技术人员可以认识到,可以对此配置进行多种修改,而不背离所要求保护的主题的范围或精神。
I.默认成员修改器
现在参考图1,一般而言,程序员使用代码生成工具或源代码生成器来为程序产生初始结构然后修改生成的源代码。然而,如果在软件开发期间有必要再次生成代码,则开发员对生成的代码的改变将被重写。因此,开发员可创建单独的源代码文件。某些源级语言便于使用单独的源文件来对生成的代码进行修改。例如,C#提供允许类和接口被分成多个片段和单独源文件的局部类型(partial type)。工具生成局部类(partial class)可在编译期间与开发员编写的局部类合并,使得每一局部类可独立创建和维护。图1示出了一编译系统。在编译期间,开发员源代码102和生成的源代码104可由编译器组件106组合。得到的实现代码108(例如可执行、中间语言)如同开发员源代码102和生成的源代码104被写入单个单元中一般。
局部类向软件开发员提供向工具生成的源代码中声明的局部类添加成员的能力,但不允许开发员修改生成的源代码中声明的成员。局部类的成员可在生成的源代码或开发员代码任一种中声明,但不能同时在两者中声明。如果对局部类的一成员被声明了一次以上,则编译器将生成冲突。
代码生成器可提供默认类成员修改器。默认成员修改器指示该成员声明将在缺乏非默认成员声明的情况中使用。因此,默认成员可用非默认类成员重写。这向软件开发员提供了在编译期间不生成冲突的情况下修改工具生成的局部类成员的一种机制。
图2是示出用于处理默认成员的方法200的流程图。于参考标号202开始,编译器接收源代码。源代码可由代码生成工具、软件开发员或两者产生。在204,检测到默认成员修改器。当编译器找到局部类中的默认成员时,在206处,编译器组件确定是否存在覆盖该默认成员的相同名字的类成员。覆盖成员可能位于分开的开发员创建源文件中。如果存在非默认或覆盖成员,则在208,编译器将利用覆盖成员的源代码并忽略默认源代码。在212,使用覆盖源代码来生成实现代码。然而,当不存在任何覆盖代码时,在210,利用默认源代码,且在212实现代码包含默认源代码。编译过程在214终止。
考虑生成的源代码文件Customerl.cs中的以下C#类声明:
partial class Customer
{
public default string Name{
get{
return name;
}
set{
name=value;
}
}
public default string Status{
get{
return status;
}
set{
status=value;
}
}
public default decimal ComputeDiscount()
{
return(Status==″Gold″?0.1M:0.05M);
}
...
}
声明了局部类Customer(顾客),它包括三个默认成员:Name(名字)、Status(状态)和ComputeDiscount(计算折扣)。希望修改Customer成员的软件开发员可在分开的源文件例如Customer2.cs中声明局部类Customer:
partial class Customer{
public string Status{
get
{
return status;
}
set
{
status=value;
if(status==″Gold″)PremierList.Add(this);
}
}
public decimal ComputeDiscount()
{
//不同于以上代码的定制计算
}
...
}
此处,开发员没有定义Name成员。因此编译器将利用生成的代码中定义的默认Name成员。开发员定义了属性成员Status和方法成员ComputeDiscount。这两个成员在名字上与在生成的源代码中声明的局部类Customer的成员相同,但包括不同或附加的逻辑。由于成员修改器默认,在软件开发员源代码中声明的成员将优先于生成的代码属性和方法。默认成员修改器允许开发员在不直接修改生成的源代码文件的情况下覆盖生成的代码中的类成员。
默认成员修改器也可在蓝图中使用。如此处所用,蓝图是可被转换成源代码的声明性语言文档(例如,可扩展标记语言(XML))。使用蓝图生成的所有局部类成员可用默认修改器来自动指定,除非默认修改器被显式关闭。考虑以下示例性蓝图:
<class name=″Northwind.Customer″table=″Customers″>
<property name=″Name″column=″ContactName″
defaultModifier=”False”/>
...
</class>
如有需要,默认修改器可通过指定诸如Customer Name属性的defaultModifier(默认修改器)的属性来显式关闭。或者,使用蓝图生成的局部类成员可被自动指定而不使用默认,除非默认修改器被显式打开。
II.用于关系管理器的代码生成模式
现在参考图3,在ORM系统中频繁使用工具生成的代码。如图3中所示,ORM系统300可包括用作应用程序组件304和数据存储组件306之间的接口的ORM组件302。关系数据可从数据存储组件306中检索,并由ORM系统302管理以供应用程序组件304使用。
常规ORM系统不能充分地提供对关系的管理。一般,有三种有问题的关系类型:一对一、一对多和多对多。在一对一关系中,类型A的每一实体对类型B的恰好一个实体具有一关系,且类型B的该实体对类型A的该实体具有相应的关系。例如,在简单的清单系统中,每一顾客A具有唯一的信用卡号,且每一信用卡号具有与之相关联的恰好一个的顾客。在一对多关系中,类型A的单个实体对类型B的一个或多个实体具有一关系,但类型B的每一实体与实体A的单个实体相关联。例如,在清单系统中,顾客可下一张或多张定单,且每一定单将与下定单的单个顾客相关联。最后,在多对多关系中,类型A的一实体对类型B的一个以上的实体具有关系,且类型B的每一实体对类型A的一个以上的实体具有关系。例如,可对多个产品下定单,且单个产品可被包括在多个定单中。
一般,面向对象语言不向软件开发员提供管理关系数据并确保关系一致性的工具。例如,当诸如顾客定单关系的一对多关系的数据使用面向对象源代码映射时,它通常如以下C#类声明中所示地映射:
class Customer
{
...
public List<Order>Orders;
}
class Order
{
...
public Customer Customer;
}
此处,源代码通过声明包括带有Customer成员的Order(定单)类以及具有成员Orders的Cutomer类,其中Orders是与Customer的一个实例相关联的定单列表。尽管以上示出的Customer和Order类的实例可用关系数据来填充,但这些类不能要求对象之间关系中的一致性。不存在防止开发员的代码修改Customer实例的定单而不更新相应定单的Customer成员的机制。当用关系数据填充对象时,程序员有责任确保对象与关系数据一致。类似地,当诸如定单的对象被移除时,程序员负责确保所有相关信息均被更新。如果Order被删除,则Order必须从相关Customer的定单列表中移除。
ORM系统可使用以下描述的代码生成模式来对一对一、一对多和多对多关系建模同时实施关系一致性。代码生成模式可包括容器组件,也被称为容器,它允许实施关系数据充分建模所需的双向关系。每一数据对象组件,也被称为数据对象(例如,Customer)可包括包含对应于该关系中的其他数据对象(例如,Order)的信息的容器。数据对象和容器可成对使用来对一对一、一对多和多对多关系建模。每一容器可包括一个或多个通知,这一个或多个通知允许容器向相应的数据对象通知数据对象之间的关系是否被修改。
容器可被实现为可对不同对象类型实例化的通用类。容器可包括一组数据对象(例如,一组定单)的聚集。包括一组数据对象的容器被称为集合容器。或者,容器可包括对数据对象的引用(例如,定单的顾客名)而非一组数据对象。这样的容器在此处被称为引用容器。一对引用容器可被用来对一对一关系建模。一对集合容器可用来对多对多关系建模。一个引用容器和一个集合容器的组合可用来对一对多关系建模。
图4是示出顾客定单关系的框图。顾客数据对象402具有包括对应于定单数据对象406的对象信息的集合容器404。类似地,定单数据对象406具有包括对应于顾客数据对象402的对象信息的引用容器408。集合容器可包括通知组件(未示出),使得对包含在集合容器404中的对象信息的改变导致向定单数据对象406发送通知。类似地,引用容器可包括通知组件(未示出),使得对包含在引用容器408中的对象信息的改变导致向顾客数据定单402发送通知。考虑以下C#代码:
partial class Customer
{
...
public EntitySet<Order>Orders;
}
partial class Order
{
...
public EntityRef<Customer>Customer;
}
此处,与顾客相关联的定单列表被实现为使用EntitySet(实体集)类的集合容器,而与定单相关联的顾客被实现为使用EntityRef(实体引用)类的引用容器。EntitySet类和EntityRef类在以下详细描述。
考虑EntitySet的示例性实现:
public delegate void Notification<T>(T arg);
public class EntitySet<TEntity>:IList,IList<TEntity>
where TEntity:class{
ItemList<TEntity>entities;
Notification<TEntity>onAdd;
Notification<TEntity>onRemove;
public EntitySet(){
}
public EntitySet(Notification<TEntity>onAdd,
Notification<TEntity>onRemove){
this.onAdd=onAdd;
this.onRemove=onRemove;
}
public void Add(TEntity entity){
if(entity==null)throw new ArgumentException();
entities.Add(entity);
if(onAdd!=null)onAdd(entity);
}
public bool Remove(TEntity entity){
if(entity==null)throw new ArgumentException();
if(entities.Contains(entity)){
entities.Remove(entity);
if(onRemove!=null)onRemove(entity);
}
}
}
此处,通用类EntitySet具有类型参数TEntity,其中TEntity被限于包括一个列表成员和两个方法成员即onAdd(添加)和onRemove(移除)的类。EntitySet构造函数具有两个参数,通知委托(delegate)onAdd和onRemove。Add和Remove方法确保通知的委托onAdd和onRemove当在EntitySet集合中添加或移除实体时被调用。EntitySet的Add方法执行基本错误检测来确保要添加的实体不为空;将实体添加到实体列表并然后使用onAdd方法向实体通知添加。类似地,为了从EntitySet中移除实体,Remove方法执行基本错误检测来确保要移除的实体不为空;从实体列表中移除该实体并然后使用onRemove方法向该实体通知移除。
EntitySet可用来实现顾客定单集合的集合容器。EntityRef可用于实现用于存储与每一定单相关联的顾客的引用容器。使用EntitySet和EntityRef实现的容器可配对来管理一对多顾客定单关系。将在以下进一步详细描述EntityRef。现在考虑利用EntitySet关系管理的上述顾客定单关系的Customer类的示例性实现:
public partial class Customer:IChangeNotifier{
...
private Entity S et<Order>_Orders;
public Customer(){
/* 以下代码提供了对要用于Add()和Remove()操作的Order对象的委
托*/
this.Orders=new EntitySet<Order>(new
System.Query.Notification<Order>(this.attach_Orders),new
System.Query.Notification<Order>(this.detach_Orders));
}
...
private void attach_Orders(Order entity){
this.OnChanging();
entity.Customer=this;
}
private void detach_Orders(Order entity){
this.OnChanging();
entity.Customer=null;
}
}
此处,由EntitySet实现的容器基础架构可用于向Customer数据对象的Orders集合添加Order。以上仅示出了生成的代码中与Orders的添加和删除相关的部分,而省略了其余代码。
类Customer包括具有定单数据对象集合的EntitySet容器。Customer类包括方法attach_Orders(附加定单)和detach_Orders(分离定单),它们更新相关联Order实体的Customer成员。对这些方法的委托作为参数被传递给EntitySet构造函数。从而,以上定义的EntitySet类的Add和Remove方法将为onAdd和onRemove使用这些方法。
图5是用于为一对多关系向容器添加数据对象而同时维护数据对象关系的程序设计方法500的流程图。具体地,将讨论上述顾客定单示例的上下文中的定单添加。在参考标号502,开始添加过程。可进行将数据对象添加到数据对象的集合的调用。例如,一个Order即Ord1可通过调用“Cust1.Orders.Add(Ord1)”被添加到顾客的实例Cust1。在504,可执行基本错误检测来确定要被添加的数据对象是否为空。如果存在错误,则在506生成异常。否则,在508,该数据对象可被添加到数据对象集合。在添加数据对象之后,在510存在添加的通知,且512该数据对象被更新来反映该关系。具体地,Ord1.Customer被设置为Cust1。该过程在514终止。
图6是用于对一对多关系从集合容器中移除数据对象而同时维护数据对象关系的程序设计方法600的流程图。具体地,将讨论上述顾客定单示例的上下文中的定单移除。在参考标号602,移除过程开始。可进行从数据对象集合中移除数据对象的调用。例如,一个Order即Ord2可通过调用“Cust1.Orders.Remove(Ord2)”从顾客的实例Cust1中移除。在604,可执行基本错误检测来确定要移除的数据对象是否为空。如果存在错误,则在606生成异常。否则,在608该数据对象可从数据对象集合中移除。在移除数据对象之后,在610存在移除的通知,且在612该数据对象被更新来反映该关系。具体地,Ord2.Customer被设置为空。该过程在614终止。
引用容器可包含对数据对象的引用而非数据对象的集合。引用容器可被认为是集合容器的更受限制形式,且包括与集合容器中存在的相同的通知。当映射一对一或一对多关系时可使用引用容器。例如,引用容器,以下被实现为EntityRef,可与使用EntitySet实现的集合容器一起使用来对上述顾客定单关系建模。考虑以下使用C#的EntityRef的示例性实现:
public class EntityRef<TEntity>:
where TEntity:class{
TEntity entity;
Notification<TEntity>onAdd;
Notification<TEntity>onRemove;
public EntityRef(){
}
public EntityRef(Notification<TEntity>onAdd,
Notification<TEntity>onRemove){
this.onAdd=onAdd;
this.onRemove=onRemove;
}
public TEntity Entity{
get{
return entity;
}
set{
if(onRemove!=null)onRemove(entity);
entity=value;
if(onAdd!=null)onAdd(entity);
}
}
}
此处,通用类EntityRef具有类型参数TEntity,其中TEntity被限于包括一个实体成员和两个方法成员即onAdd和onRemove的类。EntityRef构造函数具有两个参数,通知委托onAdd和onRemove。Add和Remove方法确保通知的委托onAdd和onRemove当在EntitySet集合中添加或移除实体时被调用。EntityRef包括具有返回实体的取(get)访问器以及设置实体值的设置(set)访问器的Entity(实体)属性。然而,设置访问器使用onAdd或onRemove方法执行通知。
图7是用于从集合容器中移除数据对象而同时维护数据对象关系的程序设计方法700的流程图。具体地,参考顾客定单示例来讨论定单的添加或移除。在参考标号702,进行更新数据对象的调用。例如,可通过调用“Order=Cust1”来更新一个Order即Ord1以添加顾客Cust1。在704,作出是要添加还是要移除数据对象的判断。如果定单要被移除,则在706通知从数据对象集合中移除数据对象,在708将值设置为空。该过程在710处终止。然而,如果定单要被添加,则在712设置值,并在714通知将数据对象添加到数据对象集合。该过程在710终止。
使用集合容器和引用容器,可对一对一、一对多和多对多关系建模。然而,为了防止通知的循环,必须建立容器对中的一个成员保持控制并管理关系的协议。如果来自容器对中第一容器的通知触发了从第二容器回到第一容器的通知则将发生循环。
可优化用于引用容器的源代码。引用容器可被认为是受限制的集合容器。引用容器的功能可在不声明单独类(例如EntityRef)的情况下实现。如在以下C#代码中所示,引用容器的功能可被移入局部类(例如,Order)的声明中以减少开销。
public partial class Order{
private EntityRef<Customer>_Customer;
public Customer Customer{
get{
return this._Customer.Entity;
}
set{
Customer v=this._Customer.Entity;
if((v!=value)){
if((v!=null)){
this._Customer.Entity=null;
v.Orders.Remove(this);
}
this.Customer.Entity=value;
if((value!=null)){
value.Orders.Add(this);
}
}
}
}
}
此处,类Order的Customer属性包括在顾客定单列表中添加或移除定单的设置访问器。设置访问器在功能上等效于上述EntityRef类的OnAdd和OnRemove方法。
优化引用容器可减少处理期间的开销,但可能导致较不简洁的源代码。然而,源代码简洁性或清楚性对工具生成的源代码而言不是重要的。
III.延期或延迟加载
对象关系映射基础架构也可允许延期或延迟加载关系数据。一般,在处理的开始处批量或在如所需基础上从数据存储组件中检索关系数据。对数据密集的程序设计而言,数据的批量检索可能要求大量存储器以便在处理期间存储数据。延缓数据的加载直到数据被引用之时,这减少所需的存储器数量。然而,数据对象应在被引用之前被加载到存储器中。
图8是示出用于执行ORM系统中的按需延期加载的方法800的流程图。开始于参考标号802,在处理期间,进行了导航至数据对象的调用。在804,截取了数据对象的导航。在806,作出关于数据对象是已经被填充且包含关系数据还是数据对象尚未被填充的判断。如果数据对象包含关系数据,则对数据对象的导航在812继续。否则,在808从数据存储组件检索对应于该数据对象的关系数据。检索可包括数据库查询或数据检索的任何其他方法。在810,使用检索到的数据来填充数据对象。对数据对象的导航在812继续。
引用和集合容器可包括允许关系数据的延期加载的加载组件。容器可截取数据对象之间的导航、查询数据存储组件并在运行时(on-the-fly)创建并填充数据对象,从而创造数据对象被完全填充的错觉而实际上不要求在使用之前将每个数据对象都加载到存储器中。
以下示例性C#代码向EntitySet类提供了附加的成员以允许延迟或延期加载:
public class EntitySet<TEntity>:IList,IList<TEntity>
where TEntity:class{
IEntitySetLoader<TEntity>loader;
...
public TEntity this[int index]{
get{
Load();
if(index<0||index>=entities.Count)throw new
ArgumentException();
return entities[index];
}
set{
Load();
if(index<0||index>=entities.Count)throw new
ArgumentException();
if(value==null||IndexOf(value)>=0)throw new
ArgumentException();
entities[index]=value;
OnRemove(old);
OnAdd(value);
}
}
public void Load(){
if(loader!=null){
entities=new List<TEntity>(loader.GetEntities());
loader=null;
}
}
public interface IEntitySetLoader<TEntity>
{
void TrackAdd(TEntity entity);
void TrackRemove(TEntity entity);
IEnumerable<TEntity>LoadEntities();
}
此处,取访问器截取对TEntity对象的导航,并调用EntitySet类的Load(加载)方法成员。Load方法检查来查看TEntity对象是否已被加载到存储器中,如果否,则创建并填充实体列表。使用取访问器截取导航确保在EntitySet的目标被引用之前即被加载。类似地,设置访问器截取至TEntity对象的导航。设置访问器也调用加载方法来确保在设置TEntity对象的值之前创建并填充了该TEntity对象。
IV.强类型表
一般,数据对象被存储在诸如数据库的数据存储系统或组件的关系数据表中。例如,在以上讨论的清单系统中,数据存储组件可包括顾客表和定单表。当向清单系统添加一新顾客或定单时,向适当的表添加一行。类似地,当从清单系统中删除一顾客或定单时,从适当的表中删除一行。
当使用面向对象程序设计构造来对来自数据存储系统的关系数据建模时,对象模型或构造应包括对应于数据存储系统的关系数据表的一组强类型数据对象表。程序设计语言构造数据对象表可以是供应用程序中使用的数据存储系统的关系数据表的表示。在清单系统示例中,生成的源代码应包括Customers表和Orders表。
应用程序对构造表的任何改变应被跟踪并存到数据存储系统表。例如,诸如Customer的类的每一新创建的实例应被跟踪,使得可通过将新的相应的Customer行插入到相应的数据存储系统表中来更新该表。类似地,Customer或其他类实例的删除应被跟踪,并被存到相应的数据存储系统表中以便删除行。
可使用通用类(例如,Table(表))来创建数据对象表。通用类可专用于处理存储在数据对象表中的不同类型的对象(例如顾客、定单、供应商、承运商...)。使用通用类来实现数据对象表利用了表的公共特征。例如,每一表可要求添加和移除数据对象的方法。考虑Table的以下示例性声明:
public class Table<T>{
public void Add(T item){
//对象关系映射基础架构跟踪要添加的项目
}
public void Remove(T item){
//对象关系映射基础架构跟踪要移除的项目
}
}
在通用类Table中定义成员Add(添加)和Remove(移除)消除了为每一个单独的数据对象表创建Add和Remove成员的必要性。
图9示出了用于创建对应于数据存储系统表的一组数据对象表的方法900。开始于参考标号902,确定数据存储系统中表的数目以及每一表的类型。在904,使用通用类实例化对应于数据存储系统表的数据对象表。在906,进行检查来确定是否存在要建模的其他数据存储系统表。如果是,则在904实例化下一数据对象表。如果已经建模了所有数据存储系统表,则该方法在908终止。
对示例清单系统,可使用一组数据对象表来创建一数据上下文以镜像表示数据存储系统表。考虑以下C#代码:
public partial class Northwind:DataContext{
public Table<Category>Categories;
public Table<Customer>Customers;
public Table<Shipper>Shippers;
public Table<Supplier>Suppliers;
public Table<Order>Orders;
public Table<Product>Products;
}
此处,使用通用类Table来创建Customers、Shippers(承运商)、Suppliers(供应商)、Orders和Products(产品)的集合。如此处所用的,Table<T>是相应的数据存储系统表的虚拟表示。生成的代码允许对各个数据对象表的Add和Remove方法进行强类型检查。生成数据对象表也省去软件开发员必须为数据存储系统的每一表定义单独的表类的工作,减少了开发员的工作和调试时间。
使用强类型表仅跟踪新创建的实例和检索到的被删除的实例。用于调用生成的代码来确保适当跟踪了新和被删除的实例的示例性方法如下所示:
Northwind myNorthwind;
...
Customer cc=new Customer{CustomerID=″ABCDE″,
CompanyName=″AcmeProducts″};
myNorthwind.Customers.Add(cc);
Customer cd=db.Customers.Element(|c|c.CustomerID==″ALFKI″);
myNorthwind.Customers.Remove(cd);
V.关系数据改变检测和跟踪
代码生成系统也允许检测和跟踪对从数据存储系统中采集到对象模型的关系数据的改变。当从数据存储系统中采集关系数据以填充由应用程序使用的数据对象时,数据对象可在无需ORM系统的知识的情况下被修改。然而,任何插入、修改或删除应被存回数据存储系统。另外,当第一应用程序处理时,其他应用程序可访问数据存储系统并修改曾用于填充由第一应用程序使用的数据对象的关系数据。当关系数据从第一应用程序存到数据存储系统时,它不应重写由其他应用程序作出的改变。
一个简单的解决方案是维护所有数据对象的副本;副本包含从数据存储系统检索到的原始数据。副本可与数据对象的当前值比较以确定数据对象是否被修改了。这导致存储器被用于存储保持不变的数据对象的相同副本。另外,当修改被存到数据存储系统时,每一数据对象与其包含原始值的副本进行比较以确定是否存在对数据对象的任何改变。处理时间浪费在比较保持不变的数据对象上。
在所公开的本主题的一个方面中,代码生成器组件可生成源级代码,它可由开发员看见并修改,以实现改变检测和跟踪。改变检测和跟踪可在源级代码中实现而非隐藏在中间格式(例如,字节码或IL(中间语言))。将实现置于源级代码使得它对软件开发员透明。
生成的代码可仅为被应用程序修改的那些数据对象创建副本。这可消除为每一数据对象创建副本的需求,并通过限制副本的数目来最小化空间开销。另外,这可消除将未经改变的数据对象与其副本进行比较来确定数据对象是否改变所需的处理时间。
现在转向图10,容器源代码可包括当数据对象将要被修改时通知ORM系统中的改变检测组件的改变通知组件。改变通知组件可利用事件处理程序。图10示出了用于跟踪经修改的对象的方法1000。开始于参考标号1002,应用程序进行更新数据对象的调用。在1006,改变检测组件检查数据对象之前是否已被复制。如果数据对象之前未被复制,则在1008改变检测组件复制数据对象,并将数据对象的副本添加到经修改数据对象的列表。如果数据对象已被复制,则原始数据对象的副本已被添加到列表且不应被重写。在1010,数据对象如应用程序所指定地来更新。考虑以下示例性C#代码:
public partial class Customer:IChangeNotifier{
private string_CustomerID;
public string CustomerID{
get{
return this._CustomerID;
}
set{
if((this._CustomerID!=value)){
//以下通知引起复制原始值
this.OnChanging();
this._CustomerID=value;
}
}
}
private void OnChanging(){
if((this.ObjectChanging!=null)){
this.ObjectChanging(this,System.EventArgs.Empty);
}
}
}
通知接口可用于指定用于改变对象的事件处理程序:
public interface IChangeNotifier{
event ObjectChangingEventHandler ObjectChanging;
}
此处,以上详细描述的Customer类内的属性的组访问器包括告知ORM系统的改变检测组件数据对象将要改变的通知。正好在数据对象实际改变之前才向ORM系统通知以允许ORM系统在数据对象被修改之前创建副本。
现在转向图11,在某时对数据对象的修改应被存回数据存储系统。图11示出了用于将改变存到数据存储系统的方法1100。开始于1102,向经修改的数据对象检索第一数据对象副本。数据对象副本包含从数据存储系统采集的原始关系数据。在1104,将数据对象副本与数据存储系统中的数据对象进行比较。在1106,进行检查以确定数据对象副本是否不同于数据存储系统对象。如果是,则数据存储系统中的数据对象已被修改,且在1108生成异常。如果数据对象副本和数据对象存储系统相同,则数据存储系统对象未经修改,且在1110经修改的数据对象被存回数据存储系统。在1112,进行检查以确定在经修改的数据对象列表中是否有附加的数据对象。如果是,则在1102检索下一数据对象副本。否则,该方法在1114终止。
参考若干组件之间的交互描述了前述系统。应理解,这样的系统和组件可包括此处指定的这些组件或子组件、所指定的组件或子组件中的某些和/或附加组件。子组件也可被实现为通信耦合至其他组件而非包括在父组件内的组件。另外,应注意到,一个或多个组件可被组合成提供聚集功能的单个组件或被分成若干子组件。组件也可与此处未具体描述但为本领域的技术人员已知的一个或多个其他组件交互。
而且,如可以理解的,以上公开系统和以下方法的各个部分可包括人工智能或基于知识或规则的组件、子组件、进程、装置、方法或机制(例如,支持向量机、神经网络、专家系统、贝叶斯置信网络、模糊逻辑、数据融合引擎、分类器...)或由其组成。这样的组件及其它可自动化某些机制或由此执行的过程来使得该系统和方法的某些部分更加自适应且高效和智能的。
考虑上述示例性系统,参考图2、5-11的流程图,可更好地理解根据所公开的主题实现的方法。尽管为说明简单起见,方法被显示并描述为一连串框,但可以理解和领会,所要求保护的主题不受框顺序的限制,某些框可按照不同的顺序发生和/或与此处描绘和描述的其他框并发。而且,不是所有示出的框都是对实现之后描述的方法所必需的。
另外,还应理解,以下并在本说明书全文中公开的方法能够被存储在制品上以便于将这样的方法传输和传送给计算机。如此处所用的,术语制品旨在包含可从任何计算机可读设备、载波或介质访问的计算机程序。
为了向所公开的主题的各个方面提供上下文,图12和13以及以下讨论旨在提供可在其中实现所公开主题的各个方面的合适环境的简要、一般描述。尽管以上在运行在一个和/或多个计算机上的计算机程序的计算机可执行指令的一般上下文中描述了本主题,但本领域的技术人员可以认识到,本发明可也与其他程序模块组合来实现。一般,程序模块包括例程、程序、组件、数据结构等,它们执行特定任务或实现特定抽象数据类型。而且,本领域的技术人员可以理解,本发明的方法可以使用其它计算机系统配置来实现,包括单处理器或多处理器计算机系统、小型计算设备、大型机、以及个人计算机、手持式设备(例如,个人数字助理(PDA)、电话、手表...)、基于微处理器或可编程消费者或工业电子产品等。所示方面也可以在分布式计算环境中实现,其中任务由通过通信网络链接的远程处理设备来执行。然而,即使不是本发明的全部方面,本发明的某些方面也可在单机计算机上实现。在分布式计算环境中,程序模块可以位于本地和远程存储器存储设备中。
参考图12,用于实现此处公开的各方面的示例性环境1210包括计算机1212(例如,台式、膝上型、服务器、手持、可编程消费者或工业电子产品...)。计算机1212包括处理单元1214、系统存储器1216和系统总线1218。系统总线1218将包括但不限于系统存储器1216的系统组件耦合至处理单元1214。处理单元1214可以是任何各种可用的微处理器。也可以使用双微处理器和其它多处理器体系结构作为处理单元1214。
系统总线1218可以是若干类型的总线结构中的任一种,包括存储器总线或存储器控制器、外围总线或外部总线和/或使用各种可用的总线体系结构中的任一种的局部总线,可用的总线体系结构包括,但不限于,11位总线、工业标准体系结构(ISA)、微通道体系结构(MCA)、扩展的ISA(EISA)、智能驱动器电子接口(IDE)、VESA局部总线(VLB)、外围部件互连(PCI)、通用串行总线(USB)、高级图形接口(AGP)、个人计算机存储卡国际协会总线(PCMCIA)以及小型计算机系统接口(SCSI)。
系统存储器1216包括易失性存储器1220和非易失性存储器1222。基本输入/输出系统(BIOS)包含有助于诸如启动时在计算机1212中元件之间传递信息的基本例程,它通常存储在非易失性存储器1222中。作为说明,而非限制,非易失性存储器1222可以包括只读存储器(ROM)、可编程ROM(PROM)、电可编程ROM(EPROM)、电可擦除ROM(EEPROM)或闪存。易失性存储器1220可以包括用作外部高速缓存存储器的随机存取存储器(RAM)。作为说明,而非限制,RAM以多种形式可用,诸如同步RAM(SRAM)、动态RAM(DRAM)、同步DRAM(SDRAM)、双倍数据速率SDRAM(DDR SDRAM)、增强型SDRAM(ESDRAM)、同步链路DRAM(SLDRAM)以及直接Rambus RAM(DRRAM)。
计算机1212也包括可移动/不可以移动、易失性/非易失性计算机存储介质。例如,图12示出盘片存储1224。盘片存储1224包括,但不限于,如磁盘驱动器、软盘驱动器、磁带驱动器、Jaz驱动器、Zip驱动器、Ls-100驱动器、闪存卡或记忆棒的设备。另外,盘片存储1224可以包括独立或与其它存储介质组合的存储介质,包括但不限于,诸如光盘ROM设备(CD-ROM)、CD可记录驱动器(CD-R驱动器)、CD可重写驱动器(CD-RW驱动器)或数字多功能盘ROM驱动器(DVD-ROM)等的光盘驱动器。为了便于将盘片存储设备1224连接至系统总线1218,一般使用诸如接口1226等可移动或不可移动接口。
可以理解,图12描述了作为用户和在合适的操作环境1210中描述的基本计算机资源之间的中介的软件。这样的软件包括操作系统1228。可被存储在盘片存储1224上的操作系统1228用来控制和分配计算机系统1212的资源。系统应用程序1230通过存储在系统存储器1216或者盘片存储1224上的程序模块1232和程序数据1234利用了操作系统1228进行的资源管理。可以理解,本发明可以使用各种操作系统或操作系统的组合来实现。
用户通过输入设备1236向计算机1212输入命令或信息。输入设备1236包括,但不限于,诸如鼠标、跟踪球、指示笔等定点设备、触摸垫、键盘、麦克风、操纵杆、游戏手柄、圆盘式卫星天线、扫描仪、TV调谐器卡、数码相机、数码摄像机、网络摄像头等。这些和其它输入设备经由接口端口1238通过系统总线1218连接至处理单元1214。接口端口1238包括,例如串行端口、并行端口、游戏端口和通用串行总线(USB)。输出设备1240使用某些与输入设备1236相同类型的端口。从而,例如,USB端口可以用于向计算机1212提供输入,并向输出设备1240提供来自计算机1212的输出信息。提供输出适配器1242来示出,存在类似显示器(例如平板和CRT)、扬声器和打印机以及其它需要专用适配器的输出设备1240的某些输出设备1240。作为说明而非限制,输出适配器1242包括提供输出设备1240和系统总线1218之间的连接手段的显卡和声卡。应该注意,诸如远程计算机1244等其它设备和/或设备系统同时提供输入和输出能力两者。
计算机1212可使用至一台或多台远程计算机,诸如远程计算机1244的逻辑连接在网络化环境中操作。远程计算机1244可以是个人计算机、服务器、路由器、网络PC、工作站、基于微处理器的装置、对等设备或其它常见网络节点等,且通常包括上文相对于计算机1212描述的许多或所有元件。为简洁起见,对于远程计算机1244仅示出存储器存储设备1246。远程计算机1244通过网络接口1248被逻辑连接至计算机1212,并且然后通过通信连接1250被物理地连接。网络接口1248包括诸如局域网(LAN)和广域网(WAN)的通信网络。LAN技术包括光纤分布式数据接口(FDDI)、铜线分布式数据接口(CDDI)、以太网/IEEE 802.3、令牌环/IEEE 802.5等。WAN技术包括,但不限于,点对点链路、类似综合业务数字网(ISDN)及其变体的电路交换网络、分组交换网络和数字用户线(DSL)。
通信连接1250指的是用来将网络接口1248连接至总线1218的硬件/软件。尽管为说明清楚,将通信连接1250示为位于计算机1212内,然而通信连接1250也可以在计算机1212外部。仅为示例的目的,连接至网络接口1248所必需的硬件/软件包括内部和外部技术,诸如包括常规电话级调制解调器、线缆调制解调器、电力调制解调器和DSL调制解调器等的调制解调器、ISDN适配器以及以太网卡或组件。
图13是本发明可与之交互的示例计算环境1300的示意性框图。系统1300包括一个或多个客户机1310。客户机1310可以是硬件和/或软件(例如,线程、进程、计算设备)。系统1300也包括一个或多个服务器1330。由此,系统1300可对应于两层客户机服务器模型或多层模型(例如,客户机、中间层服务器、数据服务器)等其他模型。服务器1330也可以是硬件和/或软件(例如,线程、进程、计算设备)。客户机1310和服务器1330之间的一种可能的通信可以是适于在两个或多个计算机进程之间传输的数据包的形式。系统1300包括可以被用来促进客户机1310和服务器1330之间通信的通信架构1350。客户机1310可操作地连接至可用来存储对客户机1310本地的信息的一个或多个客户机数据存储1360。类似地,服务器1330可操作地连接至可被用来存储对服务器1330本地的信息的一个或多个服务器数据存储1340。
以上描述的包括所要求保护的主题的各方面的示例。当然,不可能为描述所要求保护的主题而描述每个可想象的组件或方法的组合,但是本领域的普通技术人员可以认识到,所要求保护的主题的众多其它组合和排列是可能的。从而,所要求保护的主题旨在包括落入所附权利要求书精神和范围内的所有这样的变更、修改和变化。而且,就在详细描述和权利要求书中都使用的术语“包括”、“具有”或“含有”而言,当被用作权利要求书中的过渡词时,这样的术语旨在类似于解释术语“包含”的方式是包含性的。