.net之工作流工程展示及代码分享(三)数据存储引擎
数据存储引擎是本项目里比较有特色的模块。
特色一,使用接口来对应不同的数据库。数据库可以是Oracle、Sqlserver、MogoDB、甚至是XML文件。采用接口进行对应:
public interface IWorkflowDB
{
List<Flow> GetFlows();
bool SaveFlow(Flow flow);
bool DeleteFlow(Guid flowId); FlowInstance GetFlowInstanceByInstanceId(Guid flowInstanceId);
List<FlowInstance> GetFlowInstanceByFlowId(Guid flowId);
bool SaveFlowInstance(FlowInstance flowInstance);
List<FlowInstance> GetFlowInstancesListByUser(Person person = null);
bool DeleteFlowInstanceByInstanceId(Guid flowInstanceId); bool SaveForm(Form form);
bool DeleteForm(Guid formId);
List<Form> GetFormList(); bool AddStep(WorkflowStep workflowStep);
bool DeleteStep(Guid stepId);
List<WorkflowStep> GetStepList(); bool AddBaseTable(WorkflowConstant.BaseTable baseTable, Dictionary<int, string> values);
Dictionary<int, string> GetBaseTableData(WorkflowConstant.BaseTable baseTable);
bool DeleteBaseTableValue(WorkflowConstant.BaseTable baseTable, int key);
}
特色二,使用Oracle的SYS.XMLTYPE来存储数据,查询时使用辅助方法,或者XML查询表达式
这样系统里一共只用了四个表,包括一个临时表,每个表不超过三个字段。
WORKFLOW_SETTINGS表:(存储系统设置参数、流程等)
WORKFLOW_FORM表:(存储系统表单)
WORKFLOW_FLOW表:(存储流程实例)
还有一个temp表,一行一列,字段为CLOB类型。
XMLType里面存储的是什么呢?不错,正是各个类的实例,序列化后的字符:
实际存储的方式:
保存时:类的实例-->XML序列化-->直接用Oracle的XMLtype存储;
读取时:读出数据-->反序列化-->类的实例直接可用。
这样就能方便的解决实体模型或领域模型与数据库存储之间“阻抗不匹配的”的问题。而且XMLType可以被SQLServer等主流数据库支持,所以迁移到其他数据库也很方便;如果你要使用常规的建表规则进行存储,只要实现IWorkflowDB接口即可。
序列化反序列化的方法,使用了扩展方法,可以放在项目里任何一个静态类里:
public static string ToSerializableXML<T>(this T t)
{
XmlSerializer mySerializer = new XmlSerializer(typeof (T));
StringWriter sw = new StringWriter();
mySerializer.Serialize(sw, t);
return sw.ToString();
} public static T ToEntity<T>(this string xmlString)
{
var xs = new XmlSerializer(typeof (T));
var srReader = new StringReader(xmlString);
var steplist = (T) xs.Deserialize(srReader);
return steplist;
}
注意:有些类不能被序列化,比如Dictionary<TKey,TValue>,需要自己写可序列化的类。
好啦,接下来是实现IWorkflowDB接口的OracleWorkFlowDBUtility类,以存储工作流表单为例:
存储:
public bool SaveForm(Form form)
{
var formContent = form.FormContent;
var formId = form.FormId;
form.FormContent = string.Empty; var sql = string.Empty; DbHelperOra.ExecuteSql("truncate table WORKFLOW_TEMP");
var para = new OracleParameter("formInfo", OracleType.Clob);
para.Value = form.ToSerializableXML();
DbHelperOra.ExecuteSql("insert into WORKFLOW_TEMP(content) values (:formInfo)", para); var paras = new[]
{
new OracleParameter("formId", formId.ToString()),
new OracleParameter("formContent", formContent)
};
if (DbHelperOra.GetSingle("select count(*) from WORKFLOW_FORM where form_id = :formId",
new OracleParameter("formId", formId.ToString())).ToString() == "")
{
sql =
"update WORKFLOW_FORM set FORM_INFO = (select sys.xmlType.createXML(WORKFLOW_TEMP.content) from WORKFLOW_TEMP), FORM_CONTENT = :formContent where FORM_ID = :formId";
}
else
{
sql =
"insert into WORKFLOW_FORM(FORM_ID,FORM_INFO,FORM_CONTENT) values (:formId,(select sys.xmlType.createXML(WORKFLOW_TEMP.content) from WORKFLOW_TEMP),:formContent)";
} return DbHelperOra.ExecuteSql(sql, paras).ToString() == "";
}
读取:
public List<Form> GetFormList()
{
var ds =
DbHelperOra.Query(
"select t.form_info.getclobval() form_info, form_content from WORKFLOW_FORM t");
if (ds != null)
{
var forms = new List<Form>(ds.Tables[].Rows.Count);
foreach (DataRow dr in ds.Tables[].Rows)
{
var str = dr["form_info"].ToString();
var form = str.ToEntity<Form>();
form.FormContent = dr["form_content"].ToString();
forms.Add(form);
}
return forms;
}
return null;
}
删除:
public bool DeleteForm(Guid formId)
{
try
{
if(DbHelperOra.ExecuteSql("delete from WORKFLOW_FORM where form_id = :formid",
new OracleParameter("formid", formId.ToString())).ToString(CultureInfo.InvariantCulture)=="")
return true;
return false;
}
catch (Exception ex)
{
return false;
}
}
一切都很简单,没有恼人的一列列字段名,也不用ORM、代码生成器等,开发、维护效率大幅度提高。
数据访问类实例化在WorkflowService类里面
先定义私有变量:
private readonly IWorkflowDB _iWorkflowDb;
然后在类的构造函数里这么写:
public WorkflowService(IWorkflowDB workflowDb, IWorkflowMethods workflowMethods)
{
_iWorkflowDb = workflowDb;
_iWorkflowMethods = workflowMethods; }
使用简单工厂返回类的实例:
/// <summary>
/// 程序主调用方法
/// </summary>
/// <returns></returns>
public static WorkflowService GetWorkflowService()
{
IWorkflowDB iWorkflowDb;
try
{
string dbSavingProvider = WorkFlowUtility.GetConfiguration("DataBaseProvider").ToLower();
switch (dbSavingProvider)
{
case "oracle":
iWorkflowDb = new OracleWorkFlowDBUtility();
break;
case "sqlserver":
//iWorkflowDb = new SqlServerWorkFlowDBUtility();
//break;
default:
iWorkflowDb = (IWorkflowDB) Assembly.Load(dbSavingProvider).CreateInstance(dbSavingProvider);
break;
} }
catch (Exception)
{
throw new WorkFlowConfigurationNotImplementException("数据库配置失败!");
}
这样能在不同的项目中自由配置数据存储方式了。
如果要提高查询效率,比如报表,就可以这么写查询:
select rownum "序号", t.dw "单位",vt.groupname || vs.groupname "团支部",t.xh "学号",t.xm "姓名",t.xb "性别",t.csrq "出生年月", t.zzmm "政治面貌", t.zc "职称", t.gzsj "工作时间", t.lxdh "联系电话" from
(select f.instance_id,
f.instance_content.extract('//FlowInstance/Creator/@PersonId').getstringval() xh,
max(decode(x.key,'c57e4eb1-834c-491f-a1b5-b8b67f1ed160',x.value,null)) dw,
max(decode(x.key,'6008c07f-617b-4607-8fe2-6a384c75ea8a',x.value,null)) xm,
max(decode(x.key,'1153c641-7567-44ce-85c8-fcb0230d5cf7',x.value,null)) xb,
max(decode(x.key,'76936043-17e0-40b7-9adc-22e88d461082',x.value,null)) csrq,
max(decode(x.key,'22c6f1c5-d07b-45ba-9752-1118fdafee4c',x.value,null)) zzmm,
max(decode(x.key,'47b32ce0-828e-4179-bf11-2ace0028fda5',x.value,null)) zc,
max(decode(x.key,'a5cd9229-c8f9-4fb4-ba4f-e8157eeefa04',x.value,null)) gzsj,
max(decode(x.key,'9d2984f9-a5c5-474d-807a-cd756b961615',x.value,null)) lxdh
from WORKFLOW_FLOW f,
XMLTable( '/FlowInstance/FlowStepInstances/FlowInstanceStep/WriteValues/SerializableDictionary'
passing f.instance_content
COLUMNS key VARCHAR2(40) PATH 'key',
value VARCHAR2(100) PATH 'value') x
where f.instance_content.extract('//FlowInstance/Flow/@FlowId').getstringval() = 'e2f35ac0-d5aa-4413-af54-2cf4fe3759ef'
and to_date(substr(f.instance_content.extract('//FlowInstance/@CreateDate').getstringval(), 0, 10), 'YYYY-MM-DD')
between to_date('2015-01-01','yyyy-mm-dd') and to_date('2015-06-01','yyyy-mm-dd')
group by f.instance_id, f.instance_content.extract('//FlowInstance/Creator/@PersonId').getstringval()) t
left outer join vm_tuanwei_teachergr vt on t.xh = vt.gh
left outer join vm_tuanwei_studentgr vs on t.xh = vs.xh
查出来的结果如下图:
好啦,大家对我数据存储方式有什么意见,可以就此进行讨论。
本系列导航:
- .net之工作流工程展示及代码分享(预告)
- .net之工作流工程展示及代码分享(一)工作流表单
- .net之工作流工程展示及代码分享(二)工作流引擎
- .net之工作流工程展示及代码分享(三)数据存储引擎
- .net之工作流工程展示及代码分享(四)主控制类
- .net之工作流工程展示及代码分享(五)前端交互
.net之工作流工程展示及代码分享(三)数据存储引擎的更多相关文章
- .net之工作流工程展示及代码分享(四)主控制类
现在应该讲主控制类了,为了不把系统弄得太复杂,所以就用一个类作为主要控制类(服务类),作为前端.后端.业务逻辑的控制类. WorkflowService类的类图如下: 该类的构造函数: public ...
- .net之工作流工程展示及代码分享(二)工作流引擎
在介绍完表单类的时候,接下来介绍工作流引擎,主要由四个类组成,分别是流程.流程步骤.流程实例.流程步骤实例类. 流程类: [Serializable] public class Flow { [Xml ...
- .net之工作流工程展示及代码分享(一)工作流表单
Workflow表单的作用是能够在客户端进行表单设计,然后在流程中动态开放哪些输入框可以供用户填写. 在这里我扩展了一个常用的WebEditor工具——KindEditor,能够插入自定义的html符 ...
- .net之工作流工程展示及代码分享(预告)
最近在帮公司做一个工作流程序模块,要求是可以嵌入到各种现有的程序中去.我想把自己制作的思路和过程同大家分享. 先上一张结构图: 由于该项目我一个人做,所以系统结构不能太复杂. 用到的技术主要有:DDD ...
- net之工作流工程展示及代码分享(记录)
http://www.cnblogs.com/thanks/p/4183235.html
- JAVA基础代码分享--DVD管理
问题描述 为某音像店开发一个迷你DVD管理器,最多可存6张DVD,实现碟片的管理. 管理器具备的功能主要有: 1.查看DVD信息. 菜单选择查看功能,展示DVD的信息. 2.新增DVD信息 选择新增功 ...
- 一款WP小游戏代码分享
首先声明游戏是H5的代码,当然游戏部分不是我写的,感谢@LeZhi的分享,关于H5我还在学习,这里只是简单介绍一下如何把一款现成的H5游戏封装成一款WP游戏(当然也可以做成Windows游戏). 大家 ...
- Unity多媒体展示项目经验分享-ImageEffect+动态绑定
Unity多媒体展示项目经验分享-ImageEffect+动态绑定+网络通信 <ignore_js_op> “海尔科技展墙”是去年年初我们为上海家电博览会制作的一个多媒体展项,有限的工期以 ...
- Java生鲜电商平台-生鲜售后系统的退款架构设计与代码分享
Java生鲜电商平台-生鲜售后系统的退款架构设计与代码分享 说明:任何一个电商行业都涉及到退货与退款的问题,但是生鲜电商行业还设有一个显著的特点,那就是换货.在人性面前,各种各样的退货,退款,换货的售 ...
随机推荐
- http状态码详解
1 网址协议不支持的协议. 2 检测器内部错误. 3 网址格式不正确. 5 无法连接到代理服务器. 6 无法连接到服务器或找不到域名. 7 连接服务器失败. 28 操作超时.可能原因:页面执行时间过长 ...
- Socket通讯实例-基本Socket
转自:http://www.cnblogs.com/mahaisong/archive/2011/07/25/2116475.html (讲的很好,很细) 参考:http://blog.sina.co ...
- linux常用命令(一)
1.linux文件命名规则 1.除了/之外,所有字符都是可以用的. 2.有些字符最好别用,如空格,制表符,退格,@#&-,命令行操作时候可能会出现混淆. 3.避免使用.作为文件开头,linux ...
- css解决div的各种浏览器兼容性问题
方法一: min-height:500px;/*解决ie8.9.ff.chromet*/ height:100%;/*解决ie6.7*/ _height:500px;/*解决ie6超出自动溢出*/ 方 ...
- Canvas小游戏里,删除过期或者死亡元素技巧
在许多canvas游戏,canvas效果中,经常会有过期的元素需要删除 例如现在需要制作一个笨鸟先飞(flappy bird)小游戏,游戏中障碍物(且称为柱子),此时会有一个全局变量保存所有柱子的实例 ...
- jQuery 判断页面元素是否存在的代码
在原生的Javascript里,当我们对某个页面元素进行某种操作前,最好先判断这个元素是否存在.原因是对一个不存在的元素进行操作是不允许的. 例如: document.getElementById(& ...
- PHP $_SERVER['SCRIPT_FILENAME'] 与 __FILE__ 的区别 有点像static 和 self的意思 !
PHP $_SERVER['SCRIPT_FILENAME'] 与 __FILE__ 通常情况下,PHP $_SERVER['SCRIPT_FILENAME'] 与 __FILE__ 都会返回 PHP ...
- WIN7 如何将BAT文件附加到任务栏
1.桌面有个 a.bat 文件2.将a.bat 改名为 a.exe3.将 a.exe 拉到任务栏4.修改桌面的 a.exe 回 a.bat5.打开C:\Users\Administrator\AppD ...
- Preference Screen 首选项
设置Preference Screen (res\xml\userpreferences.xml) <?xml version="1.0" encoding="u ...
- struts2所有组件
struts2所有组件(动作,结果,拦截器) 动作在框架中可作为MVC模式的模型.这个角色的主要职责是控制业务逻辑,动作使用execute()方法来实现这个功能. 这个方法中的代码应该只关注与请求相关 ...