17、CSLA .NET 框架:日期处理、数据访问与工作流执行

CSLA.NET中的日期与工作流处理

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 类,开发者能够更加高效地处理日期、数据访问和工作流执行等任务,显著提升开发效率和代码质量,为构建高质量的业务应用程序提供有力支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值