CSLA .NET 框架:日期处理、数据访问与工作流执行
在开发业务应用程序时,日期处理、数据访问和工作流执行是常见的需求。本文将介绍一些实用的类和方法,帮助开发者更高效地处理这些任务。
1. 日期处理与 SmartDate 类型
在良好的 UI 设计中,用户应能自由输入任意文本,由应用程序解析其含义,日期输入尤为如此。标准的 DateTime 数据类型无法表示“空”日期,而 SmartDate 类型旨在简化业务开发者对日期的使用,并将其展示给 UI。
1.1 SmartDate 类型的特点
- 自动类型转换 :支持 String 和 DateTime 类型之间的自动转换。
- 快捷值翻译 :能将快捷值(如 +、-)转换为有效日期。
- 空日期概念 :理解“空”日期的概念,并能进行有意义的比较。
以下是 SmartDate 类型的定义:
<Serializable()> _
<System.ComponentModel.TypeConverter( _
GetType(Csla.Core.TypeConverters.SmartDateConverter))> _
Public Structure SmartDate
Implements Csla.Core.ISmartField
Implements IComparable
Implements IConvertible
Implements IFormattable
Implements Csla.Serialization.Mobile.IMobileObject
Private _date As DateTime
Private _initialized As Boolean
Private _emptyValue As EmptyValue
Private _format As String
Private Shared _defaultFormat As String
End Structure
1.2 初始化 SmartDate
SmartDate 可以通过构造函数或直接声明来创建,以下是一些示例:
Private _date1 As SmartDate
Private _date2 As New SmartDate(EmptyValue.MinDate)
Private _date3 As New SmartDate(DateTime.Today)
Private _date4 As New SmartDate(DateTime.Today, EmptyValue.MaxDate)
Private _date5 As New SmartDate("1/1/2008", EmptyValue.MaxDate)
Private _date6 As New SmartDate("", EmptyValue.MaxDate)
1.3 支持空日期
SmartDate 提供了属性来控制空日期的表示方式,并判断日期是否为空:
Public ReadOnly Property EmptyIsMin() As Boolean
Get
Return (_emptyValue = EmptyValue.MinDate)
End Get
End Property
Public ReadOnly Property IsEmpty() As Boolean Implements Core.ISmartField.IsEmpty
Get
If _emptyValue = EmptyValue.MinDate Then
Return Me.Date.Equals(DateTime.MinValue)
Else
Return Me.Date.Equals(DateTime.MaxValue)
End If
End Get
End Property
1.4 转换函数
SmartDate 提供了 StringToDate 和 DateToString 方法,用于日期和文本之间的转换:
Dim userDate As DateTime = SmartDate.StringToDate(userDateString)
| 用户文本输入 | 空值设置 | StringToDate 结果 |
|---|---|---|
| String.Empty | MinDate (默认) | DateTime.MinValue |
| String.Empty | MaxDate | DateTime.MaxValue |
| 任何可解析为日期的文本 | 忽略 | 日期值 |
2. 数据访问
数据访问是几乎所有应用程序的基本需求,除了基本的增删改查操作,还存在一些常见问题,如事务管理、处理数据库中的 Null 值以及数据复制等。
2.1 管理数据库连接和上下文
TransactionScope 是 .NET 中管理事务的强大工具,但当打开多个数据库连接时,会使用 DTC 进行管理,带来性能开销。ConnectionManager、ObjectContextManager 和 ContextManager 类可以帮助自动重用同一数据库连接。
以下是使用 ConnectionManager 的示例:
<Transactional(TransactionTypes.TransactionScope)> _
Private Sub DataPortal_Insert()
Using ctx = ConnectionManager(Of SqlConnection).GetManager("MyDb")
' insert object's data here using ctx.Connection
' ...
FieldManager.UpdateChildren(Me)
End Using
End Sub
2.2 SafeDataReader
SafeDataReader 是 ADO.NET DataReader 对象的包装器,可自动消除数据库中的 Null 值,将其转换为默认值。
Public Class SafeDataReader
Implements IDataReader
Private _dataReader As IDataReader
Protected ReadOnly Property DataReader() As IDataReader
Get
Return _dataReader
End Get
End Property
Public Sub New(ByVal dataReader As IDataReader)
_dataReader = dataReader
End Sub
End Class
2.3 DataMapper
DataMapper 模块使用反射来自动化数据映射操作,可从实现 IDictionary 的集合或具有公共属性的对象中复制数据。
Public Sub SetValue(ByVal target As Object, _
ByVal memberInfo As MemberInfo, ByVal value As Object)
If value IsNot Nothing Then
Dim oldValue As Object
Dim pType As Type
If memberInfo.MemberType = MemberTypes.Property Then
Dim pInfo As PropertyInfo = CType(memberInfo, PropertyInfo)
pType = pInfo.PropertyType
oldValue = pInfo.GetValue(target, Nothing)
Else
Dim fInfo As FieldInfo = CType(memberInfo, FieldInfo)
pType = fInfo.FieldType
oldValue = fInfo.GetValue(target)
End If
Dim vType As Type = Utilities.GetPropertyType(value.GetType())
value = Utilities.CoerceValue(pType, vType, oldValue, value)
End If
If memberInfo.MemberType = MemberTypes.Property Then
CType(memberInfo, PropertyInfo).SetValue(target, value, Nothing)
Else
CType(memberInfo, FieldInfo).SetValue(target, value)
End If
End Sub
3. Windows Workflow Foundation
WF 是 .NET Framework 3.0 以来的核心组件,可用于实现非交互式的后端处理。
3.1 从对象启动工作流
从业务对象执行工作流的基本步骤如下:
1. 创建线程同步对象。
2. 创建工作流运行时实例。
3. 设置事件处理程序。
4. 创建工作流实例。
5. 启动工作流实例。
6. 等待线程同步对象被设置。
为避免程序集之间的循环引用,可以动态加载工作流类型:
Dim workflowType As Type = Type.GetType("Namespace.WorkflowClass, Assembly")
Dim instance As WorkflowInstance = workflowRuntime.CreateWorkflow(workflowType)
3.2 WorkflowManager 类
CSLA .NET 提供了 WorkflowManager 类,简化工作流的执行过程。
| 方法 | 描述 |
|---|---|
| ExecuteWorkflow | 同步执行工作流,阻塞调用线程直到工作流停止 |
| BeginWorkflow | 异步启动工作流,调用线程不阻塞,但需在某个时刻调用 EndWorkflow() |
| WaitForEnd | 同步等待工作流停止,如果工作流已停止则立即返回 |
| ResumeWorkflow | 同步恢复工作流的执行,阻塞调用线程直到工作流停止 |
| BeginResumeWorkflow | 异步恢复工作流的执行,调用线程不阻塞,但需在某个时刻调用 EndWorkflow() |
| InitializeRuntime | 同步初始化 WF 运行时,允许调用代码在执行或恢复工作流之前操作运行时 |
| DisposeRuntime | 同步释放工作流运行时 |
graph LR
A[创建线程同步对象] --> B[创建工作流运行时实例]
B --> C[设置事件处理程序]
C --> D[创建工作流实例]
D --> E[启动工作流实例]
E --> F[等待线程同步对象被设置]
通过使用 SmartDate 类型、数据访问类和 WorkflowManager 类,开发者可以更高效地处理日期、数据访问和工作流执行,提高开发效率和代码质量。
4. 详细探讨工作流执行相关操作
4.1 同步执行工作流
使用 WorkflowManager 同步执行工作流非常简单,只需两行代码:
Dim mgr As New WorkflowManager()
mgr.ExecuteWorkflow("PTWorkflow.ProjectWorkflow, PTWorkflow")
在这种情况下,
ExecuteWorkflow()
方法会根据工作流的程序集限定名称创建一个
Type
对象,然后同步执行工作流。当该方法返回时,工作流已完成或终止,并且工作流运行时会被释放。可以通过检查
mgr.Status
来确定工作流的最终状态,其可能的值如下表所示:
| 状态 | 描述 |
|---|---|
| Initializing | 工作流正在初始化,尚未开始执行 |
| Executing | 工作流正在执行 |
| Completed | 工作流正常完成 |
| Terminated |
工作流异常终止,使用
Error
属性获取异常信息
|
| Suspended | 工作流被挂起 |
| Idled | 工作流处于空闲状态 |
| Aborted | 工作流被中止 |
4.2 处理空闲或挂起的工作流
对于可能会空闲或挂起并需要持久化到数据库的工作流,代码结构如下:
Dim mgr As New Csla.Workflow.WorkflowManager()
mgr.InitializeRuntime()
' 在这里将持久化服务与 mgr.RuntimeInstance 关联
mgr.ExecuteWorkflow("WorkflowApp.Workflow1, WorkflowApp", False)
If mgr.Status = Csla.Workflow.WorkflowStatus.Suspended Then
Dim instanceId As Guid = mgr.WorkflowInstance.InstanceId
mgr.WorkflowInstance.Unload()
' 存储 instanceId 以便稍后恢复工作流
End If
mgr.DisposeRuntime()
在这个过程中,需要先调用
InitializeRuntime()
方法创建并初始化工作流运行时,然后将持久化服务关联到运行时。例如,使用
SqlWorkflowPersistenceService
的代码如下:
Dim mgr As New Csla.Workflow.WorkflowManager()
mgr.InitializeRuntime()
Dim connectionString As String = _
"Data Source=ineroth;Initial Catalog=WorkflowPersistenceStore;" & _
" Persist Security Info=True;User ID=test;Password=test"
Dim unloadOnIdle As Boolean = True
Dim instanceOwnershipDuration As TimeSpan = TimeSpan.MaxValue
Dim loadingInterval As New TimeSpan(0, 2, 0)
' 将 SqlWorkflowPersistenceService 添加到运行时引擎
Dim persistService As New SqlWorkflowPersistenceService(connectionString, _
unloadOnIdle, instanceOwnershipDuration, loadingInterval)
mgr.RuntimeInstance.AddService(persistService)
当工作流被挂起时,需要获取其
InstanceId
并调用
Unload()
方法将其卸载,同时存储
InstanceId
以便后续恢复工作流。
4.3 加载和恢复工作流
假设
instanceId
字段包含有效的工作流 ID,以下代码可以重新加载并恢复工作流:
Dim mgr As New Csla.Workflow.WorkflowManager()
mgr.InitializeRuntime()
' 在这里将持久化服务与 mgr.RuntimeInstance 关联
mgr.ResumeWorkflow(instanceId)
ResumeWorkflow()
方法会使用
instanceId
从持久化服务中加载工作流实例,并恢复其执行。
4.4 不卸载挂起的工作流直接恢复
也可以不卸载挂起的工作流,直接在内存中恢复它。代码示例如下:
Dim mgr As New Csla.Workflow.WorkflowManager()
mgr.ExecuteWorkflow("WorkflowApp.Workflow1, WorkflowApp", False)
If mgr.Status = Csla.Workflow.WorkflowStatus.Suspended Then
' 在恢复工作流之前执行所需的任何操作
mgr.ResumeWorkflow(False)
End If
mgr.DisposeRuntime()
在这种情况下,不需要持久化服务,因为工作流从未被卸载。执行
ExecuteWorkflow()
方法时,确保工作流运行时在完成时不被释放。检查
Status
属性,如果工作流被挂起,应用程序需要执行恢复工作流之前所需的任何操作,然后调用
ResumeWorkflow()
方法恢复工作流并使其运行到完成。
5. 从业务对象执行工作流
可以使用
WorkflowManager
类简化从业务对象内部执行工作流的过程,也可以直接调用 WF 对象。通常,工作流会从业务对象的
DataPortal_XYZ
方法或工厂对象的数据访问方法中执行。
以
ProjectCloser
对象为例,它使用工作流来关闭项目:
<Serializable()> _
Public Class ProjectCloser
Inherits CommandBase
Public Shared Sub CloseProject(ByVal id As Guid)
Dim cmd As New ProjectCloser(id)
cmd = DataPortal.Execute(Of ProjectCloser)(cmd)
End Sub
Private _projectId As Guid
Private Sub New()
' 要求使用工厂方法
End Sub
Private Sub New(ByVal projectId As Guid)
_projectId = projectId
End Sub
Protected Overrides Sub DataPortal_Execute()
Dim parameters As New Dictionary(Of String, Object)()
parameters.Add("ProjectId", _projectId)
Dim mgr As New WorkflowManager()
mgr.ExecuteWorkflow("PTWorkflow.ProjectWorkflow, PTWorkflow", parameters)
If mgr.Status = WorkflowStatus.Terminated Then
Throw mgr.Error
End If
End Sub
End Class
在
DataPortal_Execute()
方法中,创建一个包含参数的字典,并将其传递给
ExecuteWorkflow()
方法。使用工作流的类型名称调用该方法,避免了可能的循环引用。执行完成后,检查工作流的状态,如果异常终止,则抛出异常。
通过这种方式,调用代码可以简单地使用
ProjectCloser.CloseProject(projectId)
来关闭项目,无需关心工作流、线程等细节,所有操作都被封装在业务对象后面。
综上所述,通过合理运用 SmartDate 类型、数据访问相关类(如 ConnectionManager、SafeDataReader、DataMapper 等)以及 WorkflowManager 类,开发者能够更加高效地处理日期、数据访问和工作流执行等任务,显著提升开发效率和代码质量,为构建高质量的业务应用程序提供有力支持。
CSLA.NET中的日期与工作流处理
超级会员免费看
37

被折叠的 条评论
为什么被折叠?



