基于OEA框架的客户化设计(二) 元数据设计
上篇 已经就客户化的整体方案进行了叙述,这次主要是说明一些细节部分的设计。
类型的视图元数据
基于OEA框架的GIX4项目中,客户化工作主要是对各客户版本中类型的视图信息进行定义。下图是包含这些类型的类图:
图1 客户化API中的类型视图元数据
属性继承
在应用程序定义中,需要支持继承类型的视图信息定义,也就是说,在基类上定义的视图信息,子类在没有定义的情况下,直接使用基类的定义;当然,也可以为具体的子类做特殊的定义。
但是,TypeViewInfo是某一个实体类型的视图信息,它只对应唯一一个Type。所以要支持继承定义,需要做一些特殊的处理。
一种方案是为所有TypeViewInfo建立父子关系,然后在获取属性值时,再按照继承线进行检索。这种方案类似于WPF中的依赖属性,不过这就意味着TypeViewInfo中所有属性的实现都不能再使用一般的.NET属性,编码起来比较复杂,代价太大。
我们在这里选用的方案比较简单,就是在所有视图信息定义完成之后,在框架内部对所有类型的值进行合并。如果某一类型自己没有定义某个值,而基类已经定义了,则直接把基类的值设置到该类型上。这种方法比较简单,而且由于这个合并的操作是在所有定义完成之后进行的,所以不需要对每个属性都进行更改,可以使用一般的.NET属性。但是,它有以下缺点:合并操作比较耗时;在合并操作前(如在定义的时候),不支持继承属性的获取。即如果这时获取某类型的定义时,并不包含父类的属性定义。不过由于比较简单,而且估计以后的使用场景也不会遇到刚才所说的情况,所以最后我们还是采用了这种方式。随便贴些代码:
public abstract class ViewInfoBase : DefinitionAtom
{
private string _label;
/// <summary>
/// 显示在视图上的“标签”
/// </summary>
public virtual string Label
{
get
{
return this._label;
}
internal set
{
this.CheckUnFrozen();
this._label = value;
}
}
/// <summary>
/// 和基类的视图信息进行合并。
///
/// 在基类上定义的视图信息,如果这个基类的子类没有显式设置其它的值,则会使用基类的视图信息定义。
/// </summary>
/// <param name="baseDef"></param>
internal virtual void MergeBaseClassDef(ViewInfoBase baseDef)
{
MergeDef(ref this._label, baseDef._label);
MergeDef(ref this._isVisible, baseDef._isVisible);
}
/// <summary>
/// 合并属性值。
/// 如果子类没有显式设置其它的值,则会使用基类的值。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="current"></param>
/// <param name="baseValue"></param>
protected static void MergeDef<T>(ref T current, T baseValue)
where T : class
{
if (current == null && baseValue != null)
{
current = baseValue;
}
}
}
应用程序定义API
之前我说过,当客户版本比较多时,定义的东西会比较多,所以客户化框架设计的目标之一就是API要尽量的简单、易用、可读。这里我们特意对API的使用方式进行了特别的设计:
- 使用强类型的方式来查找类型或进行定义。
- 使用Lambda Expression来进行强类型的属性的查找或定义。
- 方便连续为多个属性进行赋值。方式类似Web开发中的JQuery框架和.NET中的StringBuilder类。
仔细看了上篇文章的朋友可能注意到了,在Common.AppDefinition中的定义代码:
protected override UIInfo DefineUI()
{
var ui = base.DefineUI();
ui.Entity<CBFGQBQItemTitle>()
.EntityProperty(t => t.Code).ShowInLookup().ShowInList().Set_ListMinWidth(200);
ui.Entity<ContractBudget>()
.Association(cb => cb.CBMeasureItemTitles).UnVisible();
ui.Command(CCN.CopyBudgetCommand)
.Set_ToolTip("复制一条预算书").Set_Label("复制预算书").Visible();
return ui;
}
使用时,也是类似:
bool isVisible = ui.Entity<CBFGQBQItemTitle>().EntityProperty(t => t.Code).IsVisible;
if (isVisible)
{
//..........
}
小结
本篇已经把OEA中客户化设计中的主要内容讲完了,包括如果支持继承类型的视图信息定义、客户化配置API的设计。下一篇会写一下GIX4项目中客户化的一个应用实例:合同模块以插件的方式动态装配,并支持界面的自定义。
PS:最后学习了EF CTP4,发现它的配置API与我们的设计不谋而合,极为相似。虽然实现起来相对比较繁琐,但是API还是应该在以场景驱动、以客户为主的思想前提下进行设计。
- 介绍一个MonoTouch开发的伦敦官方城市指南应用
- 虾说区块链-55-《精通比特币》笔记十
- SignalR QuickStart
- Node.js入门学习笔记-IDE选择/配置之WebStorm(windows)
- Captcha插件后门分析和修复
- log4net.SignalR - 日志即时发送客户端页面
- 科学家担心的智能爆炸,真会有这一天吗?
- RSA 2018:从大会议题看2018年网络安全趋势
- Silverlight:Mouse Avoiding 躲避鼠标效果
- CTreeCtrl 控件使用总结
- 在ASP.NET MVC 4中使用Kendo UI Grid
- 每周四更面试题:True+True=?
- iis7 发布mvc 遇到的HTTP错误 403.14-Forbidden Web 服务器被配置为不列出此目录的内容
- NET中验证控件表达式汇总
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PHP实现唤起微信支付功能
- PHP封装的mysqli数据库操作类示例
- PHP-FPM 的管理和配置详解
- PHP基于curl实现模拟微信浏览器打开微信链接的方法示例
- 实例讲解PHP表单验证功能
- python如何从键盘获取输入实例
- 使用Keras实现Tensor的相乘和相加代码
- php无限级分类实现评论及回复功能
- php获取手机端的号码以及ip地址实例代码
- PHP数组遍历的几种常见方式总结
- 详解php协程知识点
- php curl简单采集图片生成base64编码(并附curl函数参数说明)
- PHP通过get方法获得form表单数据方法总结
- PHP filesize函数用法浅析
- PHP中创建和编辑Excel表格的方法