在关联关系中也需要定义级联修改检查规则。当修改校验被设置为true时,该关联关系的被引用实体的实例修改时,引用它的实体将被纳入到引用检查范围内,否则不检查该类型的实体实例是否引用了需要修改的实体实例。
服务模型
服务模型用于描述业务逻辑处理的接口信息。服务模型可以定义服务和业务操作两种接口,在模型上需要定义参数、返回值以及可能抛出的异常。同时还需要指定对数据库事务的支持方式。当一个服务接口可以有多种实现策略时,可以定义多个策略。当然策略的选择逻辑需要开发人员在随后的开发过程中通过编程实现。
服务与业务操作的差异是与软件的部署方式和应用组件的边界相关的。UBF提出了服务组的概念,一个服务组是指一组具有高度耦合性的业务组件,它们在安装部署时具有不可分割的整体性。因此当开发人员准备调用另外一个服务组内的组件提供的功能时,只能通过声明为服务类型的接口调用,而在服务组内组件间的相互调用通常使用业务操作。注意这种划分并不仅仅与是否支持远程调用有关,通常无论服务还是业务操作都支持远程调用。
界面模型
界面模型用于描述应用的交互界面。它包括表单数据模型、表单模型、参照模型及表单模版。
表单数据模型
表单数据模型(UIModel)用于开发者定义界面的数据模型。它由一个或多个视图(UIView),以及视图间的连接关系(UILink)和动作(Action)组成。
每个视图可以关联一个数据来源——目前我们仅支持实体作为数据来源,视图下包含数据项(UIField)以及这些数据项的分组(Group)和缺省的数据筛选条件。数据项通常绑定到该视图所关联的实体的属性上,当然开发者也可以建立没有任何绑定关系的数据项以用于存储交互逻辑需要的临时性数据。缺省的数据筛选条件是开发者在设计阶段用OQL定义的缺省数据加载条件,可以通过代码动态的修改。
动作用于开发者设计界面的功能行为,通常UBF的设计工具会缺省地预置一些常用行为,如保存、删除、查找等。
表单模型
表单模型(Form)用于开发者定义用户界面,如显示内容、布局、前端控制行为等。在表单模型中UBF提供了30种是用于ERP软件开发领域的控件,其中有数据录入型的控件也有前端行为控制用的关联控件。每个控件都有数据来源的绑定信息,通常都来源于表单数据模型,特殊情况下开发者可以不绑定UIModel,在代码中实现控件内容的管理。
表单模型上由关于Portal整合时使用的表单关联方面的信息。其中Form参数用于定义表单接收的参数,而提供者集合用于定义表单可以提供的参数。
参照模型
参照模型是轻量的表单数据模型和表单模型的复合体。因为大多数参照页面无论是界面风格还是表单数据模型的结构都具有相似性,唯一定义的仅仅是表单数据模型中视图所关联的实体和数据项以及过滤条件,因此UBF提供了这个简化版的模型以方便开发工作。
如果开发者需要定义一些特殊的参照页面,可以使用表单数据模型和表单模型像开发页面一样去开发参照页面。
表单模版
表单模版用于表单模型的重用目的。通常有一些页面大体相似,仅有一些局部的差异,开发者可以为相同的部分设计表单并生成表单模版,然后在开发表单模型时套用该模版。注意模版套用是一种复制动作,因此对模版的任何修改都将不会影响已经套用了该模版的表单。同时在一个表单上只能在模型创建时进行模版套用动作。
应用组装模型
应用组装模型用于描述应用的整体结构。包括应用领域的多级划分,每个应用下包含的前后台组件、应用的功能菜单结构、应用的页面。
应用模型
用于软件开发者以树状结构规划其开发的软件产品。开发者将其开发的业务组件和界面组件规划到每个应用中。作为支持组件化开发和交付的平台,UBF为软件开发者提供了从应用功能和客户价值角度描述其软件产品的模型。依据该模型所提供的信息,软件开发者可以实现相应的安装和许可证策略,以及组件间接口衔接的策略,为达到按需交付提供有力的支持。
页面模型
页面模型用于定义在Portal上显示的Page的信息。每个页面上可以定义一个或多个表单,页面内的布局支持条带式布局方式,即一个页面内可以有一个或多个条带,每个条带内可以放置多个表单,按次序从上向下自然排布。条带的宽度由开发者指定,不会因内部表单的尺寸而发生伸缩,但高度不能确定,由所有内部表单的高度综合确定。
在页面模型内可以定义表单间的关系,即将一个表单的提供者参数与另一个表单的接收参数间建立绑定关系。这样当提供方表单的数据发生变化时可以引发接收方产生相应得变化。
UBF运行平台
基础服务
UBF底层是一组基础服务支撑UBF运行平台和其上的应用软件的运行。包括上下文管理、配置服务、日志服务、国际化异常框架、悲观锁服务、服务会话管理、资源服务、Cache服务、数据库连接服务、数据库事务管理、OQL引擎、表达式引擎、事件引擎和异步调度引擎。
服务会话管理
服务会话管理工作在Portal或应用服务器的服务线程上,监视服务线程的进入和退出。
提供统一的线程静态变量存储管理,在离开线程时负责清理。
提供线程级的缓存管理;
提供线程上的服务或业务操作调用栈监控,以防止出现无限制的迭代调用。
提供服务或业务操作的上下文环境,并在其中提供服务级的缓存管理。
上下文服务
工作在服务会话上,提供应用的上下文环境管理。UBF维护基本且必须的应用上下文,包括企业、组织、登录用户、登录日期、当前语言文化、用户登录的会话标识符。这些上下文信息初始来自用户登录时,UBF将其缓存在Portal的session中,并响应用于的请求时设置到工作线程上。当调用远程服务时,UBF负责将上下文传递到远端的工作线程上。
开发人员可以增加自定义的上下文信息。
悲观锁服务
悲观锁服务提供非等待的并发控制机制——即一旦加锁不成功将立即返回。UBF提供了进程内和分布式两种实现策略,当仅部署一个应用服务器时应当配置使用进程内实现策略,所有的锁都在进程内管理,以提供最佳的运行效率。当部署为多服务器集群时,应当配置使用分布式实现策略,所有的锁信息都在数据库表中管理。
悲观锁支持共享和独占(读/写)两种锁定级别。共享锁允许有多个所有者同时锁定同一个对象,以用于防止读取已经过期的数据。独占锁仅允许一个所有者加锁,以用于变更数据定意图。
悲观锁允许同一所有者对同一对象反复加锁,当然也要进行相应次数的解锁。当所有者已经拥有锁的情况加,可以通过加独占锁进行锁升级,但有可能加锁失败。
事件引擎
事件引擎用于实现事件发布订阅机制,以满足某些业务需求。
事件订阅可以是临时或持久的。
事件处理器可以指定过滤条件,仅当条件满足时,事件处理器才会被调用。
事件处理器可以被同步或异步地调用。
事件处理器的错误处理行为可以指定为容错或是立即报告。
所有这些事件处理器的行为都可以通过属性(Attribute)或是订阅参数来指明。
开放的体系结构,可以很容易扩充基于消息的,分布式的事件系统功能。
异步调度引擎
调度引擎用于满足后台的定时任务需求。
标准异步调用,具有可实时查询状态,可靠性等功能增强。
任务定时调度,可实时查询请求执行状态,任务定义持久化。
灵活的周期性定时策略,可随意组合年,月,周,日,小时,分,秒七级定时单位。 支持即时启动策略
每个定时单位可以按区间,周期间隔,离散时间点集合,定点时间四种策略指定。
支持多种可执行逻辑的表达,包括委托,指定方法名,定制实现接口。
开放的体系结构,可扩展定时策略、可执行逻辑接口等等。
国际化异常框架
模型设计阶段设计的业务异常,在生成代码时UBF将其生成为符合国际化异常框架的异常类,同时也支持编程方式实现。
任何符合国际化异常框架的异常UBF可以将其透明地传递到远端。
依据当前上下文中语言文化的标识或去相应语言的信息
数据库连接服务
UBF运行平台支持多企业、多组织的应用在同一个应用服务器实例上运行。而不同的企业不能对应相同的数据库。因此UBF的数据库连接服务将依据当前上下文中企业的信息获取正确的数据库连接,而不用开发人员关注多企业导致的多数据库问题。
实体开发框架
实体(BE)的基本概念
BE,即Business Entity,指领域模型中的业务数据对象,如:订单头,客户,地址,国家等BE的设计工作通过UBF Studio完成。在一个模型图中,我们会设计实体,属性类型,枚举类型,关联,继承,组合,效验,异常,事件等等相关的东西。
实体的对外结构基本组成
每一个强类型的实体都有以下几个类:
实体类,如A_Ass1to1
实体的Key(强类型的EntityKey): A_Ass1to1.EntityKey
实体的查询类Finder:A_Ass1to1.EntityFinder
实体的强类型集合EntityList:A_Ass1to1.EntityList
实体的资源属性和强类型访问属性的辅助类
实体弱类型的EntityKey
弱类型EntityKey是BusinessEntity的内部类,也是强类型EntityKey的基类,主要涉及标识一个实体两个关键的属性:ID和EntityType,并对外提供一个GetEntity()的公开方法
实体内部数据存储
在实体内部,自身的数据为基本类型,保存在集合InnerData中,关联数据为对象或集合类型,保存在集合InnerRelation中。这两个对象都提供了一些事件和方法,如GetValue/SetValue,GetRelation/SetRelation等用来对实体进行较高级的控制(如弱类型操作)。由于强类型的方式使用BE比较简单直观,在生成实体代码时,会提供强类型的访问方式,所以,开发人员一般不会直接访问实体的内部数据
实体的CopyTo
为了支持实体数据的复制,可以利用ICopyable接口,基类Entity支持该接口,可以通过实例方法CopyTo,将实体中的数据复制给target。
CopyTo对外提供两种方法:
CopyTo(Entity target),这种方式默认是不拷贝ID
CopyTo(IPersistableObject po, bool isCopyKey),这种方式可以由第二个参数决定是否要拷贝ID
注意:
目前CopyTo方法只完整实现了对基本属性的拷贝,对于关联实体,实际上是不拷贝的,对于一对一或一对多的情况,会出现关联对象也拷贝的假象,实际上,对象是懒加载上来的,对于一对多的情况,是空对象,以后根据实际的使用情况,决定是不是要完整实现关联实体的拷贝
CopyTo方法只是将相关的业务数据拷贝,不涉及到一些系统的控制属性,如SysState,NeedPersistable等系统的控制字段,不涉及到OriginalData
实体的OriginalData
实体带有一个OriginalData的属性,保存实体在数据库中的原始值,OriginalData反映的是实体在数据库中的映像,初始值是一个空的实体对象,只有在查询,新建和修改操作成功后,才会刷新OriginalData,保持和数据库一致,需要详细说明的是,新建和修改时刷新OriginalData的动作在基类的OnInserted事件和OnUpdated事件之后,所以,在生成的XXX Extend.cs文件中,如果在后事件中要访问旧值,需要注意前后顺序
通过实体的GetChangedAttributes方法可以返回变化后的简单属性的集合,注意是简单类型,不包括集合属性,属性类型。
示例:
using (ISession s = Session.Open())
{
Yel_Ass1to1_A a = Yel_Ass1to1_A.Create();
19400bd7240c844769eaee2f = "OldValue";
19400bd7240c844769eaee2fmit();
a = Yel_Ass1to1_A.Finder.Find("ID = " + a.ID);
19400bd7240c844769eaee2f = "NewValue";
Assert.AreEqual(1, a.GetChangedAttributes().Count);
a.Code = 123;
Assert.AreEqual(2, a.GetChangedAttributes().Count);
19400bd7240c844769eaee2f = "aaa";
Assert.AreEqual(2, a.GetChangedAttributes().Count);
19400bd7240c844769eaee2f = "OldValue";
Assert.AreEqual(1, a.GetChangedAttributes().Count);
a.Remove();
19400bd7240c844769eaee2fmit();
}
这里有几个需要提醒开发人员注意的事项:
OriginalData刷新的时机:
对于新建和修改操作,OriginalData是在OnInserted/OnUpdated方法运行完后刷新,对于查询,OriginalData是在查询后刷新,对于删除,OriginalData没什么意义,取决于删除对象的加载方式,简单说,就是在OnSetDefaultValue/ OnInserting/ OnInserted中,新建对象的OriginalData是一个id为0的空实体对象,在OnSetDefaultValue/ OnUpdating / OnUpdated中,修改对象的OriginalData是旧对象

