U9-UBF应用开发手册2011

更新时间:2023-04-25 19:49:01 阅读量: 医药卫生 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

用友U9-UBF应用开发手册

V2.5

前言

UAP(Universal Application Platform)是用友公司为开发新一代面向服务(Service-Oriented Architecture, SOA)的世界级商业应用套件产品(U9)而精心打造出来的ERP软件生产平台。通过UAP平台,使企业信息资源变得可重用、透明化,并且系统具有高可扩展性,让业务处理更加高效、简洁、安全。UAP平台为用户提供了一个统一的集成开发环境,用户可以使用包括模型设计、UI设计、报表设计、规则设计、数据库设计、BI设计等各方面的设计器,并通过可视化的界面和友好的交互操作,自动生成用户所需要的各种功能控件。使得大型的企业级商业应用软件第一次实现了技术与业务关注点的分离,并且通过快速的动态业务建模与服务组装技术,实现了企业动态业务的快速部署与应用,真正实现了“随需而变”的实时企业与全球商务的企业信息化价值理念。

UAP(Universal Application Platform)平台是用友软件经过多年的技术积累和知识沉淀,在微软.NET相关规范和标准的基础上,提供完全支持基于领域语言(DSL)的模型驱动开发(MDD)模式,为各种复杂的企业级商业应用系统提供专业、安全、高效、可靠的开发、部署和运行企业管理应用软件的开发工具平台。它主要包括:应用运行平台(UBF)、应用开发平台(UBF Studio)和组件化发布平台。

UBF(UFIDA Business Framework)

实现与操作系统、数据库、.Net Framework、Office、WMI、.Net Compact Framework、MSMQ 等底层核心技术的调用与协作,通过屏蔽底层的复杂实现,提高企业应用软件的灵活性、可扩展性和开放性。

针对开发ERP软件的特点,提供了一套适用的类库、框架以及具有扩展性的通用解决方案。有效地降低了开发工作的难度和工作量。

在系统交付、安装和部署后,支撑业务系统的解析和执行;提高应用软件的可定制性与可集成性。提供对OFFCIE、移动商务、第三方软件系统等企业级的集成与应用协同。

UBF Studio

提供了统一的集成开发环境,其中包括模型设计、UI设计、报表设计、规则设计、数据库设计、BI设计等各方面的设计器,通过可视化的界面和友好的交互自动产生需要的各种软件工件,极大地提高了软件开发的效率和质量。提供对完整产业链的全角色开发的支撑环境。 组件化发布平台

提供软件产品的组件规划工具,以定义软件产品的工艺图。自动化构造工具将依据该工艺图,自动地构造组件并存入组件库中。安装系统生成工具将按照用户的意图从组件库中提取适当的组件产生安装包。

在本手册中我们将详细介绍怎样使用UAP中的UBF和UBF Studio开发应用。

UBF概述

UAP平台与应用系统间的整体逻辑架构

UAP平台是在国际上主流和公认的技术标准与规范的基础上建立的一个开放的企业级开发工具平台。它采用了元数据驱动的、面向服务的体系架构,并提供了统一的编程抽象模型,是一个适合应用软件开发及部署的全角色平台。UAP平台与应用系统之间的逻辑关系与整体架构如下图所示。

其中,UAP平台提供了模型定义、服务组装、应用开发集成环境(UBF Studio)、应用平台以及应用工具等五个核心的工具集。并通过这五大工具集为应用系统以及第三方的其它应用提供统一的模型定义、功能开发与应用集成的环境。

UAP平台的技术体系架构

UBF的技术体系结构采用分层的架构模式,主要可以分为数据层、业务层、表示层,并且通过抽象的控件模型提供对多种客户端的应用支持。整个架构如下图所示:

其中,在数据层中,持久化服务引擎主要负责访问和查询存储在数据库中的各种业务数据,在隔离业务层和数据存储管理的同时,实现与业务层的实时交互。持久化服务的这种隔离有以下好处:

——减少数据库提供者变更带来的影响;

——减少因数据对象变更带来的影响(如变更数据库的schema);

——封装数据的处理操作,这将在很大程度上减少测试和维护工作;

——通过O/R映射机制,以维护对象和持久存储之间的一致性,减少因面向对象和非面向对象这两种技术存在着阻抗不匹配。

在业务层中,业务实体对象封装了一个业务中的元数据、存储过程和触发器以及该业务的规则、过程或事件。业务实体对象是业务中实际存在的事物或概念,是对“ER”模型中概念的面向对象的扩展。业务实体对象负责执行包括强制的业务规则、应用规则、数据有效性、并发和存储等所有方面的内容。且多个独立的但有关联关系的业务实体对象可以一起协作来完成一个应用,完成不同的任务需执行很多具有不同特点的业务实体对象。

而业务服务则可以定义为一段独立的逻辑程序,当多个服务组合在一起时可完成不同类型的业务需求。服务描述了贯穿业务的工作流程和信息,同时对业务逻辑进行了封装,实现了对业务实体对象的操作,并驱动业务实体完成业务功能。服务可以由工作流系统、业务实体对象管理器、面向对象语言或交互过程定义系统实现。通过UDDI服务网关来查询、绑定内部或外部相应的服务或应用,并调度相应的一个或多个业务实体对象来实现业务处理。而业务流程对象封装了业务处理与业务策略过程。例如,一个定单处理工作流组件可能结合客户、定单等业务实体对象完成定单处理的工作流程。

在表示层中,通过MVC的模式建立业务模型、视图以及控制器之间的业务连接,并实现对各种客户端界面(包括基于浏览器的WEB应用方式、用户交互的窗体以及Smart Client等应用方式)的支持。每个窗体用来显示系统提供的信息以及传递用户的输入信息。这种基于窗体的用户界面包括两种类型的组件:

——用户界面组件:基于.NET Framework的组件,包括Smart Client组件和Web Form组件,还支持用户基于.NET Framework定制的组件。

——用户界面处理组件:复杂的用户界面通常需要很多非常复杂的窗体。为了提高其可复

用性、可维护性和可扩展性,需要创建分离用户界面处理的组件,以封装窗体和界面导航之

间的相关逻辑。可以对一个窗体中组件之间的依赖、确认和导航应用相同的概念。这些UIP 组件通常是一些基于诸如:Front Controller, Application Controller等设计模式的定制组件。UI和UIP组件之间的交互通常采用MVC模式。

另外,UBF技术体系架构中还包含基础服务层:即提供其它所有层都能使用的一系列基础服务。这些服务分成三类:

——安全:提供与应用和系统安全相关的服务集合;

——执行控制管理:这些服务负责管理组件或服务以及相关的资源,还负责处理容错和可扩展性等操作和控制的需求;

——通信:提供组件或服务之间的通信,包括.NET Remoting、SOAP、同步或异步消息等服务。

UBF领域模型语言(DSL)

为了提供对模型驱动的软件开发技术的有效支持,UBF台提供了一种领域特定语言(DSL),其中包括了业务领域语言、表单领域语言、流程领域语言以及报表领域语言等。并针对不同的领域语言采用不同的模型化以及组件化的生成方式,例如通过业务领域语言,可以有效地建立实体模型、数据模型以及服务模型,并且根据模型的关键属性与特征生成相应的软件组件。通过多种模型生成的各种相关的软件组件在应用组装语言的支持下实现动态组装,从而快速形成一个完整的应用系统。

其中:

版型

是扩展业务实体定义的描述方法,是对业务对象进行分类识别的工具,主要用来对业务模型进行抽象,找出实体间的公共属性;每个版型可附带一个代码片段作为模版,根据业务需要由设计人员动态创建,在实体定义阶段进行引用。通过设置版型,对实体进行标识,从而易

于识别,并可基于版型进行分类。比如:帐表类实体等树形实体,可通过建立版型进行识别。 特性

可在不同实体间复用的属性集和版型集;可复用的属性集和版型集通过实体转存为特性,在维护实体属性和方法的时候通过引用特性引入已保存的特性。

模式:

可在不同组件间复用的实体集,以及实体间的关系。

实体模型

实体模型用于描述业务数据的结构和关系。实体模型族中包括实体组件、实体、属性类型、数据传输对象、动态枚举、异常、实体校验器、事件和关系。其中关系分为继承、组合和关联。

实体组件

实体组件与软件行业通常所说的组件的概念并不相同,实际是用于描述一组具有强依赖关系的实体的边界。在一个实体组件内仅能有一个主要实体及其组合的实体。UBF的持久化引擎使用实体组件的元信息以保证实体组件内主实体与其组合实体的生命周期的一致性。

实体

实体模型用于开发者定义应用的数据模型。实体模型中包括属性和方法。实体分为主实体和非主实体,其中只有主实体才能组合非主实体,而不能被组合。

在实体模型上需要指定实体在数据库上存储时的数据库表的表名。如果该实体继承于其他实体,还需要指定这种继承关系在数据库上的存储方式,目前UBF仅支持单表继承——即基类的数据也将存储在具体的实现类对应得表中。为了优化实体数据的加载和保存效率,开发者还应当在实体上建立一个索引项,并仔细地规划索引项中应当包含的实体的属性和次序。

实体模型上还有用于通用查询服务的标志,如果开发人员设置了该标志,则通用查询服务将可以展现该实体的数据。

如果开发者设计了一个仅用于继承的抽象实体,需要设置抽象类标志。

实体的属性

实体属性是关于实体中数据项的描述模型。它的基本信息包括名称、类型、显示名和缺省值。实体属性模型中有一组关于校验的信息用于持久化引擎对数据的合法性进行校验,如可空标志、只读标志、字符串的长度以及数值类型的值范围等。

实体属性模型中与持久化有关的信息包括业务主键、一旦使用不可修改、国际化、是否敏感

日志字段。其中如果声明为业务主键则该属性将成为该实体的唯一约束的一部分,只有当实体对象上所有业务主键属性的值组合没有重复时,该实体对象才能成功地增加。国际化用于指定字符串类型的属性是否支持多语编辑和保存。一旦使用不可修改标志用于类型为其他实体——引用关系,被设置后表明该实体对象所引用的其他实体对象将不能被修改。是否敏感日志字段标志用于指定该属性的改变是否做系统得变更记录。

实体属性还可以被指定为计算列,并能定义计算表达式。计算列不会被存储到数据表中。

关联实体可见和服务可见标志用于指定属性的可见性,只有被设置的属性才能被关联实体访问或作为服务的参数。

而查询属性标志则,表示该属性是否可以被通用查询服务所展示。

实体上可以指定任意数目的可开发者设计的校验器,以保证业务数据的合法性。

实体的方法

实体方法是关于实体中行为的描述模型。开发者除了可以指定名称、显示名称和返回值类型等基本属性外,还可以指定可见性——如public、protected等,以及静态、虚方法和重载方法。

实体的方法模型上可以声明任意数量的异常,以表明该方法将可能抛出这些业务异常。

实体的版型

开发者可以为实体指定一个或多个版型。

属性类型

属性类型是一种没有独立生命周期的特殊实体。它可以有属性和方法,但没有校验器。属性类型模型没有持久化相关的信息,不能被持久化到独立的表中。它的数据只能被存储到使用它的实体的表中,相当于嵌入在实体中的复合数据。

数据传输对象

数据传输对象是可以远程传输的特殊实体。但不能被持久化到数据库中。在数据传输对象中其属性的类型如果是实体类型,则应当指定是实体的类型本身还是实体Key类型。通常应当指定为实体Key类型。

动态枚举

动态枚举是一种既可以在设计期指定枚举值,也可以在运行时动态增加枚举值的数据类型。

实体校验器、事件和异常

用于定义实体的业务校验器和业务异常信息以及业务处理过程中发出的业务事件。

关系

关系的模型用于定义实体间的关系。这包括继承关系、组合关系和关联关系。组合关系只能用于实体组件内部,而关联关系只能在实体组件间使用。

像数据库的表设计一样,组合和关联关系可以定义为一对多、一对一、多对多关系。

在关联关系中需要定义级联删除规则。当级联删除标志置为True时,表示需要对关联关系的被引用实体在删除时做级联删除检查,否则不做任何控制。如果级联删除规则为NoAction,表示如果要删除的实体被引用,则将不能被删除;如果级联删除规则为SetNull,表示如果实体被引用,则当它引用的实体被删除时,引用方改为空值;如果果级联删除规则为Cascade,表示如果要删除的实体已经被引用,则连同引用者一起删除。当是否启用级联校验置为True时,如果将要删除一个实体的实例时时需要将该关系上引用被删除实体的实体纳入到规则控制范围内,否则不检查改实体的实例是否引用了将要被删除的实体。

在关联关系中也需要定义级联修改检查规则。当修改校验被设置为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是旧对象

为什么我访问某实体对象的OriginalData的属性为空

首先,看OriginalData对象的ID,如果为0,表示是一个空对象,所有的属性为默认值,请参考OriginalData刷新的时机判断这个访问操作是不是新建,这个对象是标准的查询出来的还是通过CopyTo的方式构造的(实体基类的CopyTo不涉及OriginalData)

如果OriginalData有有效的ID,检查这个对象的加载方式,看看是不是特殊的加载方式引起属性为空

OriginalData的关联对象

OriginalData 和CopyTo方法很类似,只完整实现了对基本属性的拷贝,对于关联实体,实际上是不拷贝的,这样,出现aEntity.OriginalData.bEntity的访问方式,对bEntity,是懒加载上来的

实体的状态SysState

实体的状态,为枚举对象,开发人员主要关心以下4个枚举值:Inserted,Updated,Deleted,Unchanged,分别对应实体的新建,修改,删除,不变化(查询返回的初始状态)

是否需要持久化NeedPersistable

NeedPersistable表示这个对象是否需要持久化,如果为false,则这个对象将不会参与持久化

Session

基本概念和使用方式

在现在UBF中,Session的本意是work unit,即持久层的一个边界,非常轻,主要用作批量提交,并标识这次批量提交的边界。在session里面的CUD操作会受持久层控制,而seesion 外实体是不受控制的,所以,做CUD操作要在session里面完成,一般建议一个BP调用最好都有一个大session包住,这样,在里面的操作就不用多次开Session,性能会好些

关于事物,session仅仅是一个持久层边界,不涉及到事务等概念,目前UBF的事物支持在AOP上定义,可以在BP上设置

当发生session嵌套的情况时,每次提交都是真正提交

using (ISession session1 = Session.Open ()){

...

using (ISession session2 = Session.Open ()){

...

19400bd7240c844769eaee2fmit(); //提交更改,但只是Session2范围内的修改更新 }

19400bd7240c844769eaee2fmit(); //提交更改,只处理Session1的修改更新

}

当前ISession可以通过Session的Current属性获得,每调用一次Session的Open方法,Current属性都会被更新

在一个Session范围内,可以分步提交,

using(ISession session1 = Session.Open ()){

Ass1to1 objA1 = A_Ass1to1.Create();

19400bd7240c844769eaee2fmit(); //提交objA1的新建动作

Ass1to1 objA2 = A_Ass1to1.Create();

19400bd7240c844769eaee2fmit();//提交objA2的新建动作

}

对外接口

Create(IEntity entity)

将entity对象的状态设置为UFSoft.UBF.PL.Engine.ObjectState.Inserted,并加入到当前的session中

Modify(IEntity entity)

将entity对象的状态设置为UFSoft.UBF.PL.Engine.ObjectState. Updated,并加入到当前的session中

Remove(IEntity entity)

如果当前session有这个entity,且状态为insert,则从当前session中移出这个对象,其他情

况下,将entity对象的状态设置为UFSoft.UBF.PL.Engine.ObjectState. Deleted,并加入到当前的session中

InList(IEntity entity)

将entity加入到当前的session中

objA = A_Ass1to1.Create();

19400bd7240c844769eaee2f_A_1to1 = "Test";

using (ISession s = Session.Open())

{

s.InList(objA);

19400bd7240c844769eaee2fmit();

}

Assert.IsNotNull(A_Ass1to1.Finder.Find("ID = '" + objA.ID + "'"));

DeList(IEntity entity)

将entity从session中移出

using (ISession s = Session.Open())

{

objA = A_Ass1to1.Create();

objA.Code_A_1to1 = 333;

s.DeList(objA);

19400bd7240c844769eaee2fmit();

}

Assert.IsNull(A_Ass1to1.Finder.Find("ID = '" + objA.ID + "'")); 实体的操作

增加实体

简单实体:

模型如下,对实体A操作

◆常规做法:

通常我们新建实体是要求在session里面建立:

using (ISession session = Session.Open())

{

obj = A_Ass1to1.Create();

...

19400bd7240c844769eaee2fmit();

}

◆特殊做法:

遇到一些特殊情况,需要在session外面new一个对象时。这种做法很特殊,需要显式在一进入sesssion时调用session.Create(obj),不推荐

A_Ass1to1 obj = new A_Ass1to1();

using (ISession session = Session.Open())

{

//一进入session,操作对象之前(包括设置实体的状态,赋值等所有的相关的操作之前)

//要显示调用session.Create(obj)

session.Create(obj);

obj.ID = 111;

19400bd7240c844769eaee2f_A_1to1 = "aaa";

19400bd7240c844769eaee2fmit();

}

主从实体:

◆1对1,其中A_Com1c1为主实体,B_Com1c1为非主实体,注意红字的写法

A_Com1c1 objA;

B_Com1c1 objB;

using (ISession session = Session.Open())

{

objA = A_Com1c1.Create();

objA.Code_A_Com1c1 = 1000;

19400bd7240c844769eaee2f_A_Com1c1 = "objA";

objB = B_Com1c1.Create(objA);

objB.Code_B_Com1c1 = 1001;

19400bd7240c844769eaee2f_B_Com1c1 = "objB";

19400bd7240c844769eaee2fmit();

}

◆1对多:其中A_Com1cN为主实体,B_Com1cN为非主实体,注意红字的写

法。对于非主实体,有两种新建方式,这两种方式都只支持

A_Com1cN objA;

B_Com1cN objB;

using (ISession session = Session.Open()) {

objA = A_Com1cN.Create();

objA.Code_A_Com1cN = 111;

//方式一

objB = B_Com1cN.Create(objA);

objB.Code_B_Com1cN = 222;

objB = B_Com1cN.Create(objA);

objB.Code_B_Com1cN = 333;

//方式二

objB = objA.B_Com1cN.AddNew();

objB.Code_B_Com1cN = 444;

objB = objA.B_Com1cN.AddNew();

objB.Code_B_Com1cN = 555;

19400bd7240c844769eaee2fmit();

}

查询实体

(关于OQL和参数的用法,见实体查询这个章节)

查单个实体:下面的操作为查询一条ID为“111”的纪录

FindByID方法是先从缓存里面取,缓存没有再去数据库取,

A_Com1cN obj = A_Com1cN.Finder. FindByID(111);

Find方法是直接从数据库取

A_Com1cN objA = A_Com1cN.Finder.Find("ID = 111");

查实体集合

FindAll方法是直接从数据库取,参数为OQL语句

A_Com1c1.EntityList list = A_Com1c1.Finder.FindAll("");

修改实体

简单实体

◆常规做法

通常我们修改实体是要求在session里面建立

using(ISession session = Session.Open ()){

Customer obj = Customer.Finder. FindByID(id);

if(obj!=null){

19400bd7240c844769eaee2f = "new name";

}

... ...

19400bd7240c844769eaee2fmit();

}

◆特殊做法

遇到一些特殊情况,需要外面传进的实体时,需要显式调用session.

Modify(obj),不推荐

using (ISession session = Session.Open())

{

session.Modify(obj);

19400bd7240c844769eaee2fmit();

}

主从实体:

◆1对1

◆1对多

using (ISession session = Session.Open())

{

objA = A_Com1cN.Finder.Find("ID = '" + objA.ID + "'", null);

B_Com1cN.EntityList bList = objA.B_Com1cN;

bList[0].Name_B_Com1cN = "aaa";

bList[1].Name_B_Com1cN = "bbb";

19400bd7240c844769eaee2fmit();

}

删除实体

简单实体

using(ISession session = Session.Open ()){

Customer obj = Customer.Finder.FindByKey(id);

if(obj!=null){

obj.Remove();

}

... ...

19400bd7240c844769eaee2fmit();

}

主从实体:

删主实体会自动全部删除从实体

using (ISession session = Session.Open())

{

objA = A_Com1cN.Finder.Find("ID = '" + objA.ID + "'", null);

//会自动删除objA及objA下所有的从实体

objA.Remove();

19400bd7240c844769eaee2fmit();

}

只删除从实体:

删除从实体有两种方式,一种是调用从实体的objA.B_Com1cN.RemoveAt(0),一种是调用objA.B_Com1cN.Remove(b),这两种方式是等价的

using (ISession session = Session.Open())

{

objA = A_Com1cN.Finder.Find("ID = '" + objA.ID + "'", null);

B_Com1cN.EntityList bList = objA.B_Com1cN;

B_Com1cN b = bList[2];

objA.B_Com1cN.RemoveAt(0);

objA.B_Com1cN.Remove(b);

19400bd7240c844769eaee2fmit();

}

Q&A

◆对于一对多组合,我怎样知道删除的子对象

我们知道,对于一对多的情况,当我们对子对象调用Remove方法时,该对象会从当前的集合中移出,即子对象的集合存放的是有效的对象,是新建和修改的对

象。如果我们还需要访问删除的对象,可以从当前的List的属性DelLists中找到。

◆对一对多组合的子对象作集合Clear操作,怎么没有删除

需要注意,实体的删除动作只能用Remove方法,Clear是集合本身的方法,不是持久层提供的删除自对象的方法

实体的查询

BE的查询这块主要有3个查询类,EntityDataQuery,EntityQuery和EntityViewQuery,EntityDataQuery 主要用于希望返回结果是IDataReader,DataSet,单值的情况EntityQuery 主要用于希望返回的结果是实体,实体集合

EntityViewQuery 主要用于对视图的操作,与前2种不同的是,需要自己处理数据库的连接。EntityDataQuery

创建方式:

有2种方式创建EntityDataQuery

通过Entity对象:

EntityDataQuery q = Yel_Ass1to1_A.Finder.CreateDataQuery();

通过EntityFullName:

EntityDataQuery q = new EntityDataQuery("Association.Yel_1to1Self");

EntityDataQuery q = new EntityDataQuery(Yel_1to1Self.EntityRes.BE_FullName);

返回DataSet:FindDataSet

全OQL方式

DataSet ds = q.FindDataSet("select ID,Name,Code from Association::Yel_1to1Self");

条件OQL方式

DataSet ds = q.FindDataSet( "ID>1");

返回IDataReader:FindDataReader

全OQL方式

IDataReader dr = q.FindDataReader ("select ID,Name,Code from Association::Yel_1to1Self");

条件OQL方式

IDataReader dr = q.FindDataReader ( "ID>1");

返回单值:

q.FindValue("select Max(ID) where id > 111");

分页查询

DataSet ds = q.FindDataSetByPage(1, 2, " ID > @ID and Name = @mmm order by ID"); EntityQuery

创建方式:

有2种方式创建EntityQuery

通过Entity对象:

EntityQuery eq = Yel_Ass1to1_A.Finder.CreateQuery();

通过EntityFullName:

EntityQuery eq = new EntityQuery ("Association.Yel_1to1Self");

EntityQuery eq = new EntityQuery(Yel_Ass1to1_A.EntityRes.BE_FullName);

另外,以强类型的方式使用Yel_Ass1toN_A.Finder,其对外接口和功能与EntityQuery 类似,只是Yel_Ass1toN_A.Finder返回强类型的对象,EntityQuery返回弱类型的对象

返回实体:FindByID 和Find

FindByID ,先从缓存加载对象,如果缓存没有,则从数据库加载对象

Yel_Ass1toN_A a = Yel_Ass1toN_A.Finder. FindByID (a.ID);

Yel_Ass1toN_A a = (Yel_Ass1toN_A)eq.FindByID(a.ID);

Find方式,直接从数据库加载

本文来源:https://www.bwwdw.com/article/ftbq.html

Top