带工作流基础 (WF) 的最佳实践架构






4.68/5 (11投票s)
本文演示了如何将 WF 用作业务层。
下载 BestPracticeArchitectureWWF.zip
何时选择工作流基础
- 适用于长期运行的业务流程。
- 经常变化的业务逻辑或规则。
- 需要流程的视觉模型/执行时。
引言
在这篇文章中,我将演示如何使用 WWF 构建企业应用程序。您会找到很多关于 Windows Workflow Foundation 的学习资料,但很难找到如何以及在哪里将其集成到架构中。
架构图
看看架构图,UI 通过 WCF 服务与业务层交互。WF 用作业务层的一部分。您也可以将 WF 服务放在 WCF 前面,但那样 UI 就不知道 WCF 的实体了。根据最佳实践,不应将任何内容直接暴露给 UI 层,因此将 WF 作为库放在 UI 层不是一个好主意。人们通常使用 WF 来处理业务规则、状态持久化、事务或一些耗时流程,因此我建议将其放在业务层。在我附加的示例中,为了简单起见,我没有涉及实体框架和数据库部分。但当然,您可以添加带 POCO 类的实体框架。这方面的文章很多,所以我认为您在实现时不会遇到任何困难。将来我也会提供一些相关的示例。
背景
为了实现此应用程序,我将使用以下技术:
-
Microsoft Visual Studio 2010
-
Microsoft .NET Framework 4 的更新 4.0.3 – Visual Studio 2010 SP1 的设计时更新 (KB2600214)
要实现此项目,人员应具备 .NET 技术基础知识,如 C#、ASP.NET、WCF、WF。
概念验证示例
添加一个名为“BestPracticeArchitectureWWF”的空白解决方案。
业务层 (C# 库)
向现有解决方案添加 C# 库项目。在“Class1.cs”中为方法添加以下代码。此方法将接受一个参数,并对其进行一些修改后返回。这是您可以通过实体框架调用数据库的地方。
public static string BusinessMethod(string parm1)
{
return parm1 + " Returned by Business layer.";
}
业务层 (WF Activity)
向现有解决方案添加 WF Activity 库项目。
在 Activity1.xaml 中添加流程图。
将 Sequence 拖到指定位置。
选择 Sequence,并在参数选项卡中声明 2 个参数,如下图所示。一个用于接受参数,另一个用于返回。
现在,将另外两个活动拖到 Sequence 中,如上图所示。一个是 Assign 活动,另一个是 Primitive 组面板中的 Invoke Method。在 Assign 活动中,会将 Argument2 变量赋值给 Argument1 + “Modified in WF”。Invoke Method 用于调用业务 C# 库。添加对 C# 库的引用。要调用库方法,请将 Target Type 设置为类。方法名直接键入“BusinessMethod”。无需填写 TargetObject(TargetType 和 TargetObject 中需要填写一个)。一件奇怪的事情是,即使您在 Activities 表达式中处理 C# 项目,它也只接受 vb 代码。
最终,此活动的 XAML 将如下所示。无需修改 XAML 中的任何内容,但有时重命名某些内容会导致 XAML 中未更改,这时我们需要查看。
<Activity mc:Ignorable="sap" x:Class="ActivityLibrary1.Activity1"
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation>"
xmlns:b="clr-namespace:BusinessClassLibrary1;assembly=BusinessClassLibrary1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System"
xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:s1="clr-namespace:System;assembly=System"
xmlns:s2="clr-namespace:System;assembly=System.Xml"
xmlns:s3="clr-namespace:System;assembly=System.Core"
xmlns:s4="clr-namespace:System;assembly=System.ServiceModel"
xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities"
xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities"
xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation</a>"
xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System"
xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel"
xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.Core"
xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=mscorlib"
xmlns:sd="clr-namespace:System.Data;assembly=System.Data"
xmlns:sl="clr-namespace:System.Linq;assembly=System.Core"
xmlns:st="clr-namespace:System.Text;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:Members>
<x:Property Name="argument1" Type="InArgument(x:String)" />
<x:Property Name="argument2" Type="OutArgument(x:String)" />
</x:Members>
<sap:VirtualizedContainerService.HintSize>654,676</sap:VirtualizedContainerService.HintSize>
<mva:VisualBasic.Settings>Assembly references and imported namespaces
for internal implementation</mva:VisualBasic.Settings>
<Flowchart sad:XamlDebuggerXmlReader.FileName=
"C:\ManojWork\Article\BestPracticeArchitectureWWF\BestPracticeArchitectureWWF\ActivityLibrary1\Activity1.xaml"
sap:VirtualizedContainerService.HintSize="614,636">
<sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">False</x:Boolean>
<av:Point x:Key="ShapeLocation">270,2.5</av:Point>
<av:Size x:Key="ShapeSize">60,75</av:Size>
<av:PointCollection x:Key="ConnectorLocation">300,77.5 300,127.5</av:PointCollection>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<Flowchart.StartNode>
<FlowStep x:Name="__ReferenceID0">
<sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<av:Point x:Key="ShapeLocation">200,127.5</av:Point>
<av:Size x:Key="ShapeSize">200,51</av:Size>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<Sequence sap:VirtualizedContainerService.HintSize="264,352">
<sap:WorkflowViewStateService.ViewState>
<scg3:Dictionary x:TypeArguments="x:String, x:Object">
<x:Boolean x:Key="IsExpanded">True</x:Boolean>
</scg3:Dictionary>
</sap:WorkflowViewStateService.ViewState>
<Assign sap:VirtualizedContainerService.HintSize="242,58">
<Assign.To>
<OutArgument x:TypeArguments="x:String">[argument1]</OutArgument>
</Assign.To>
<Assign.Value>
<InArgument x:TypeArguments="x:String">[argument1 + "Modified in WF"]</InArgument>
</Assign.Value>
</Assign>
<InvokeMethod sap:VirtualizedContainerService.HintSize="242,130"
MethodName="BusinessMethod" TargetType="b:Class1">
<InvokeMethod.Result>
<OutArgument x:TypeArguments="x:String">[argument2]</OutArgument>
</InvokeMethod.Result>
<InArgument x:TypeArguments="x:String">[argument1]</InArgument>
</InvokeMethod>
</Sequence>
</FlowStep>
</Flowchart.StartNode>
<x:Reference>__ReferenceID0</x:Reference>
</Flowchart>
</Activity>
构建 WF Activity 并添加对 WCF 服务的引用。
服务层 (WCF)
添加 WCF 服务。在 Service1.svc.cs 中添加以下代码来调用 activity。不要忘记在 IService1.cs 中更新契约。
public string GetData(string value)
{
ActivityLibrary1.Activity1 ca = new ActivityLibrary1.Activity1();
ca.argument1 = value.ToString();
IDictionary<string, object> dr = new Dictionary<string, object>();
dr.Add("argument1",value);
var output = WorkflowInvoker.Invoke(ca,dr);
string st = output["argument2"].ToString();
return string.Format("You entered: {0}", st);
}
此方法从 UI 获取字符串变量,并在 WF Activity 和 C# 业务库中进行处理。构建 WCF 服务。
UI (ASP.NET)
向现有解决方案添加 Asp.net 项目。在 ASP.NET 的位置,您也可以使用 Silverlight/WPF 项目。如果您想了解如何在 Silverlight 项目中调用 WCF,请参阅我的其他文章。
向此项目添加服务引用。现在转到 Default.aspx,按照以下代码添加一些控件。
<h2>
WF Calling Sample
<br />
<asp:Label ID="Label2" runat="server" Text="Enter Your Name: ">
</asp:Label><asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<br />
<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
<br />
<asp:Label ID="Label1" runat="server" Text=""></asp:Label>
</h2>
为按钮添加点击事件。在代码隐藏中,我们将调用 WCF 服务。
protected void Button1_Click(object sender, EventArgs e)
{
Service1Client sr = new ServiceReference1.Service1Client();
int i = 0;
if (int.TryParse(TextBox1.Text, out i))
{
Label1.Text = sr.GetData(i);
}
else
Label1.Text = "Please enter Number only.";
}
将 Web 应用程序设置为启动项目,将 Default.aspx 设置为启动页。运行应用程序。输入名称并单击按钮,即可从 WF Activity 和 C# 业务库获取修改后的内容。希望这个想法能帮助您设计应用程序。
摘要
在这篇文章中,您已经学会了如何将 WF Activity 利用到业务层。希望这是一个相当简单的示例,可以阐述架构。
如果本文对您设计/编码应用程序有所帮助,请不要忘记投票。请评论您的建议和改进意见。
祝您编码愉快!!