使用 Windows Workflow Foundation 构建基于图像的工作流





0/5 (0投票)
了解如何使用 WF 和 Pegasus Imaging 的活动库构建一个小型文档图像工作流应用程序(第 1 部分)。然后探索如何使用 Visual Studio 2008 将 Pegasus 的活动用于向 MOSS 添加图像工作流(第 2 部分)。
随着 Windows Workflow Foundation (WF) 的发布,一种基于工作流的应用程序开发新范式应运而生。工作流不是将业务逻辑嵌入代码中,而是以声明方式在可视化设计器中构建,使用称为活动的构建块功能。应用程序开发人员仍然必须依赖领域专家准确描述他们正在自动化的业务流程。然而,这些专家在编写第一行代码的那一刻并不会被抛在后面。领域专家和利益相关者受益于 WF 图形设计器,它提供了代码无法实现的应用程序可见性;他们不仅可以看到所遵循的流程,还可以验证工作流的准确性。对于开发人员而言,WF 提供了长期运行工作流应用程序通常所需的大量基础设施,包括进程和线程灵活性、补偿和事务。本质上,WF 在开发和业务部门之间架起了一座桥梁,同时提供了足够的技术服务,使该技术物有所值。微软选择将 WF 用作 Microsoft Office SharePoint Server (MOSS) 中的工作流引擎,并将在下一版 BizTalk 中添加该引擎,这足以说明其价值。
当然,如果没有实际执行工作的活动,就不可能在 WF 中构建复杂的工作流。WF 和 .NET 3.0 附带了许多有用的活动;然而,大多数都是通用活动,不能解决特定的业务问题。微软已将构建领域特定活动的任务留给最了解如何构建它们的人——个体开发人员和领域专家。在图像领域,Pegasus Imaging Corporation 凭借超过 15 年的图像工具包提供商经验,提供 WF 活动。在未来一年中,将发布用于文档图像清理、条形码读写和 OCR 的活动,供您自己的工作流应用程序或托管 WF 运行时(如 MOSS)的应用程序使用。
为了展示 WF 和 Pegasus Imaging 活动库的强大功能和灵活性,本文的第 1 部分将展示如何使用 WF 构建一个小型图像工作流应用程序。第 2 部分将展示如何使用 Visual Studio 2008 将 Pegasus 的活动用于向 MOSS 添加图像工作流。
第 1 部分:使用 Pegasus 的图像处理活动构建工作流应用程序
一个常见的图像工作流场景是文件夹监控。在此场景中,持续监控一个文件夹路径以获取新创建的图像文件,这些文件通常来自扫描仪或其他网络位置。到达此位置的图像可能因捕获过程而受到某种程度的损坏(页面可能倾斜,图像因扫描仪床上的灰尘斑点而脏污等),并且需要在进一步处理之前进行清理。构建一个自动化此清理的应用程序的最大挑战是确定实际需要的清理——并非所有清理步骤都适用于所有图像。然而,WF 使实际的处理定义变得非常容易,特别是当我们编写了应用程序框架之后。
应用程序框架 — 托管 WF 运行时
为了演示文件夹监控场景的潜在解决方案,我们将使用 Visual Studio 2008 构建一个小型**顺序工作流控制台应用程序**。此应用程序将从命令行获取要监控的文件夹路径,开始监视该文件夹中的新文件,并在每个新文件到达时,使用 WF 运行清理工作流。为了构建我们的应用程序,我们将不得不自行托管 WF 运行时,因此在进入可视化设计器之前,我们需要进行一些设置和编码。
首先,启动 Visual Studio 2008,并打开**新建项目**对话框。在**Visual C#**标题下,选择**Workflow**作为项目类型,然后选择**Sequential Workflow Console Application**作为项目模板。为您的项目输入一个名称,我们就可以开始了。
x
项目创建完成后,您可能会看到一些对您来说很新的东西,特别是如果您是第一次使用 WF。我们不会深入探讨 WF 的所有细节,但有几件事需要注意。**顺序工作流控制台应用程序**模板创建了两个类——`Program` 和 `Workflow1`——它们分别包含应用程序的主入口点和默认 WF 工作流。如以下清单 1 所示,`Program` 中生成的 `Main` 函数创建了一个 `WorkflowRuntime` 实例(管理我们工作流的运行时引擎),并创建了我们的默认工作流 `Workflow1` 的新实例,返回一个 `WorkflowInstance` 实例来表示我们已加载的工作流。最后,已加载的实例被启动,我们的工作流开始执行。但是有一点需要注意:实例是在工作线程上创建的,因此对 `Start` 的调用会立即返回。因此,我们必须使用一个等待句柄,在 `WorkflowCompleted` 或 `WorkflowTerminated` 处理程序中发出信号,以确保我们不会在工作流有机会完成之前从 `Main` 返回而杀死进程。
清单 1:使用顺序工作流控制台应用程序模板生成的 Visual Studio 主函数
namespace ImageCleaner
{
class Program
{
static void Main(string[] args)
{
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted +=
delegate(object sender, WorkflowCompletedEventArgs e)
{
waitHandle.Set();
};
workflowRuntime.WorkflowTerminated +=
delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(ImageCleaner.Workflow1));
instance.Start();
waitHandle.WaitOne();
}
}
}
}
在简要介绍了 WF 之后,是时候构建一些实际的东西了。首先,我们需要重新排列 `Main` 中的默认代码,以便能够处理通过命令行传递的路径并设置文件夹监视器。幸运的是,Microsoft 通过在 .NET Framework 中包含 `FileSystemWatcher` 类型,使监视文件夹变得非常容易。我们唯一需要做的就是设置我们希望监视的路径,然后为 `Created` 事件安装一个事件处理程序。其他事件可用于修改、删除和属性更改,但我们暂时忽略这些。`FileSystemWatcher` 初始化后,我们可以实例化我们的 `WorkflowRuntime` 实例,注册我们的 `WorkflowCompleted` 和 `WorkflowTerminated` 处理程序,最后开始监视新文件的创建。下面的清单 2 显示了对 `Main` 所做的修改,但当您看到一些 Visual Studio 生成的部分缺失时,请不要担心。它们很快就会重新出现。
清单 2:修改 Main 以处理命令行并设置文件夹监视器
// our workflow runtime instance - this object must be disposed
static WorkflowRuntime workflowRuntime = null;
static void Main(string[] args)
{
// validate our command line for proper usage
if (args.Length != 1)
{
Console.WriteLine("Usage: ImageCleaner.exe <Full Path of Folder to Watch>");
return;
}
// get the path to watch and ensure it is a valid path
// before we move forward...
string folderToWatch = args[0];
if (System.IO.Directory.Exists(folderToWatch) != true)
{
Console.WriteLine("Command line argument \'" +
folderToWatch + "\' is not a valid path");
return;
}
// create a file system watcher to begin watching the folder
// passed via the command line
FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();
fileSystemWatcher.Path = folderToWatch;
// setup our listener to monitor changes
fileSystemWatcher.Created +=
new FileSystemEventHandler(OnFileSystemWatcherCreated);
// initialize the runtime in this program
using (workflowRuntime = new WorkflowRuntime())
{
// ensure we handle the completed and terminated events
workflowRuntime.WorkflowCompleted +=
new EventHandler<WorkflowCompletedEventArgs>(OnWorkflowCompleted);
workflowRuntime.WorkflowTerminated +=
new EventHandler<WorkflowTerminatedEventArgs>(OnWorkflowTerminated);
// start watching
fileSystemWatcher.EnableRaisingEvents = true;
// the watcher will continue watching until we are told to stop watching
Console.WriteLine("Press \'q\' to quit watching the folder...");
while (Console.Read() != 'q')
{
; // do nothing - continue to process and wait
}
}
}
现在 `Main` 已完成,我们需要重点充实 `Created` 事件触发时所需的动作。使用 `FileSystemWatcher` 类型时,非常重要的一点是仔细思考您如何知道文件已完全存在于正在监视的文件夹位置。事件在写入文件第一个字节时立即触发,随后是 `OnChanged` 事件,通知我们额外的文件操作,例如追加。因此,您必须使用某种启发式方法来知道文件已完全写入磁盘。下面的清单 3 展示了我们的处理程序和选择的启发式方法——查找正在写入文件的独占锁定的代码。一旦我们获取了锁定,我们就知道文件可用。如果我们在指定的超时(我们选择了 5 分钟)内未获取锁定,我们将退出循环并抛出异常。这种启发式方法不支持许多常见情况——例如文件写入时间超过 5 分钟的情况——因此您应根据具体需求进行适当修改。
清单 3:处理程序和工作流实例创建
static void OnFileSystemWatcherCreated(object sender, FileSystemEventArgs e)
{
// Get the current time to test timeout
DateTime t0 = DateTime.Now;
// By default, the file is not completely available
bool fileAvailable = false;
// Stop checking the file if the Timeout duration is exceeded
TimeSpan Timeout = new TimeSpan(0, 5, 0);
while (DateTime.Now - t0 < Timeout)
{
try
{
// If the file can be opened exclusively, then it is available
using (FileStream fs = new FileStream(e.FullPath, FileMode.Open,
FileAccess.Read, FileShare.None))
{
fileAvailable = true;
break;
}
}
catch (IOException)
{
// The file is still being written, wait and try again
System.Threading.Thread.Sleep(100);
}
}
// If the file is still not available, then a timeout occurred
if (!fileAvailable)
{
throw new ApplicationException(
string.Format("File lock timeout: {0}", e.FullPath));
}
// the file is available...so, we can process that file
ProcessFile(e.FullPath);
}
一旦文件可用,我们就调用 `ProcessFile`,如清单 4 所示。正如所承诺的,创建表示我们工作流的 `WorkflowInstance` 的代码再次出现。该代码与我们开始这个项目时 Visual Studio 为我们生成的代码大致相同。主要区别在于我们将新创建的文件路径 (`InputFile`) 和我们希望创建的“已清理”文件路径 (`OutputFile`) 传递给我们的工作流实例。这些值 (`InputFile` 和 `OutputFile`) 用于设置 `ImageCleanerWF` 类型中包含的属性,并且名称必须完全匹配,否则将抛出异常。最后,因为我们的工作流正在创建一个新文件,所以如果文件夹中新创建的文件正是我们刚刚创建的文件,我们需要立即返回。
清单 4:ProcessFile
// our wait handle to ensure we don't exit the application prematurely
static AutoResetEvent waitHandle = new AutoResetEvent(false);
// the last file we created via the workflow
static string lastWorkflowOutputFile;
static void ProcessFile(string file)
{
// we don't want to process the file we just created
if (file == lastWorkflowOutputFile)
return;
// make sure our wait handle is non-signaled
waitHandle.Reset();
// determine the file extension of the file so we can
// create a new one for our sample output
string fileExtension = Path.GetExtension(file);
string newFileText = "_clean" + ".pdf";
// create a dictionary to pass the path to our workflow
Dictionary<string, object> myParams = new Dictionary<string, object>();
myParams.Add("InputFile", file);
myParams.Add("OutputFile", file.Replace(fileExtension, newFileText));
// create a new instance of our workflow and begin to process
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(ImageCleaner.ImageCleanerWF), myParams);
instance.Start();
// wait until we're signaled to continue - we only want to process one workflow
// at a time for now
waitHandle.WaitOne();
}
我们尚未触及的应用程序的一个重要方面是线程。我们不会深入探讨这个话题;但是,您应该知道 WF 的构建并非考虑到特定的线程模型。相反,它允许宿主应用程序通过调度程序决定 `WorkflowInstance` 如何运行。默认情况下,当创建 `WorkflowRuntime` 时,`DefaultSchedulerService` 会被安装,并导致每个工作流实例都在线程池中的一个线程上运行。对于从应用程序主线程启动工作流的应用程序来说,这个默认设置非常完美。然而,如果我从线程池线程(而不是应用程序的主线程)启动工作流,使用 `DefaultSchedulerService` 将导致从线程池中请求第二个线程,这不是很可扩展。为了解决这个问题,您可以使用 `ManualSchedulerService` 在调用线程上运行工作流。在 ASP.NET 应用程序中,使用这种技术托管 WF 运行时很常见,如果您仔细观察,我们的文件夹监视器也可以从这种优化中受益。`FileSystemWatcher` 事件在线程池线程上处理,因此我们当前的实现会导致请求第二个线程池线程。对于演示目的来说没问题,但在您尝试扩展时会受到限制。
在线程讨论结束后,最后,清单 5 展示了 `WorkflowCompleted` 和 `WorkflowTerminated` 的处理程序。我们必须处理这些事件,以便正确地发出等待句柄的信号。否则,将导致死锁。
清单 5:WorkflowCompleted 和 WorkflowTerminated 处理程序
static void OnWorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
{
// get the names of the files we just processed\created
string inputFile = (string)e.OutputParameters["InputFile"];
string outputFile = (string)e.OutputParameters["OutputFile"];
// store the name of the output file, so we make sure we don't create again
lastWorkflowOutputFile = outputFile;
Console.WriteLine("\"" + inputFile + "\" has been cleaned and output as \"" +
outputFile + "\"");
waitHandle.Set();
}
static void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message + ": " +
e.Exception.InnerException.Message);
waitHandle.Set();
}
受够了框架的繁琐!实际上,如果你退一步思考我们到目前为止所取得的成就,我们用很少的代码完成了大量工作。我们有一个运行正常的应用程序,它监控我们选择的文件夹,并托管一个强大的工作流引擎,该引擎获取我们的工作流定义,将其加载到内存中,为我们的工作流创建一个运行线程,并安排其执行。呼!但我离题了;现在是时候享受乐趣了——通过拖放来达到图像处理工作流的完美境界。
使用 WF 设计器定义图像处理工作流
在 WF 带来的所有好处中,最大的优势也许是在技术和业务部门之间建立的桥梁。虽然可以在代码中构建 WF 工作流,但一图胜千言——以图形化设计器中的流程图式布局表示业务流程,可以提供代码中无法实现的可见性。那么,让我们开始吧……
当我们创建**顺序工作流控制台应用程序**项目时,项目模板创建了一个名为 `Workflow1` 的基于代码的工作流,我们后来将其重命名为 `ImageCleanerWF`。当您在解决方案资源管理器中双击我们的工作流时,您将看到设计图面加载,并在工具箱中显示可用的 WF 活动列表。
要在 WF 中定义工作流,我们组织称为**活动**的可重用功能块,并通过这些活动的输入和输出来将它们链接起来。开箱即用的活动性质相当通用,虽然有用,但不能解决领域特定的问题。然而,像 Pegasus Imaging 这样的第三方被鼓励用他们自己的领域特定解决方案来补充这些开箱即用的活动。图 2 还展示了 Pegasus Imaging 随我们的 ScanFix Xpress 5.1 文档图像清理工具包提供的一部分活动。有关 ScanFix Xpress 5.1 和其他 Pegasus Imaging 工具包中包含的活动的完整列表,请访问此处
现在,让我们开始构建我们的图像处理工作流。
在我们的场景中,我们正在监视一个文件夹,以获取来自扫描仪或其他来源的新传入文件。这些图像需要某种清理——在我们的例子中是去倾斜和去斑点——然后我们想将输出保存为 PDF 文件。首先,我们将一些活动拖放到设计器图面上,从 `LoadImage` 活动开始。然后,因为 ScanFix Xpress 5.1 仅适用于位图(即黑白)图像,我们需要添加一个 `IfElse` 活动来检查正确的图像类型。如果加载的图像可以被 ScanFix 处理,我们将使用 `Deskew` 和 `Despeckle` 活动对其进行处理,然后使用 `SaveImagePDF` 活动将其保存到磁盘为 PDF。否则,我们将跳过清理并保存。将活动以适当的方式排列后,设计器图面将如下图 3 所示。
至此,我们通过在设计图面上排列 WF 活动,定义了业务流程的控制流。但是,我们还没有完成。如果您仔细查看图 3,您会看到一系列带有下拉列表的感叹号,告诉您有些地方不太对劲。这种反馈由单个活动通过称为验证器的东西提供。在我们的例子中,我们活动的验证器告诉我们将活动链接在一起,以便数据在它们之间流动。我们使用数据绑定将活动链接在一起。
数据绑定允许我们以声明方式指定特定数据块的来源。为了演示绑定如何工作,我们将设置 `LoadImage` 活动上的 `FileName` 属性。如果您回想上面清单 4,我们向工作流传递了两个参数——`InputFile` 和 `OutputFile`。我们希望将 `FileName` 属性设置为 `InputFile` 参数中传递的值,这通过图 4 所示的对话框完成。要从 Visual Studio 启动此对话框,请选择 `LoadImage` 活动,然后在属性设计器中,双击 `FileName` 标题框中的 `Bind Property` 图标。
至此,直觉占据主导,解释变得次要。您所要做的就是选择树中的 `InputFile` 项,然后单击“确定”。瞧!我们现在已将 `LoadImage` 活动的 `FileName` 属性链接到 `InputFile` 参数。对于那些在 Visual Studio 中跟着操作的人,一旦设置了 `FileName` 属性,您也会看到设计器中的感叹号消失。这是因为 `LoadImage` 活动的验证器知道该活动已正确初始化。
现在 `LoadImage` 活动已准备就绪,我们可以通过链接输入和输出来继续其他活动。我们不会逐一介绍每个数据绑定,但是此工作流有一个重要的方面我们尚未涉及——`ifElseBranchActivity1` 的 `Condition` 活动。此活动的验证器要求设置 `Condition` 属性,以便可以决定选择哪个分支。可以通过代码或使用规则以声明方式定义此条件。为了保持简单,并避免深入讨论 WF 中的声明性规则,我们将使用代码。您只需在 `Condition` 下拉列表中选择**Code Condition**,然后指定方法签名。此条件的实际代码显示在清单 5 中。
清单 6:条件评估的代码
private void OnBitonal(object sender, ConditionalEventArgs e)
{
if (this.loadImage.OutputImage.PixelFormat
== System.Drawing.Imaging.PixelFormat.Format1bppIndexed)
{
e.Result = true;
}
}
如图所示,该方法确定加载的图像是否为 1 位图像。如果是,我们将执行 `ifElseBranchActivity1` 中包含的活动;否则,将运行 `ifElseBranchActivity2` 中的活动。有了这最后一部分,我们就得到了业务流程的完整图形表示——而且这一切都只需要 8 行代码,如果我们使用声明性规则,甚至不需要这些代码。这效率高的流程设计怎么样!
第 2 部分:通过 Visual Studio 2008 在 SharePoint 中使用 Pegasus 的图像处理活动
第 1 部分展示了 WF 工作流运行至少需要两个部分——WF 运行时的应用程序宿主和工作流本身。对于那些从头开始编写自己的 WF 应用程序的人来说,您将不得不以某种形式或方式编写代码来宿主 WF 运行时。您可能选择像我们在第 1 部分中那样将运行时宿主在控制台应用程序中、Windows 服务中,甚至 ASP.NET 应用程序中。但是,如果您的目标是为已为您宿主 WF 运行时的现有应用程序编写工作流,那么我们根本无需担心运行时宿主。我们所要担心的是开发我们的工作流,并在与我们目标应用程序集成时成为一个好公民。目前,我们的主要目标是 Microsoft Office SharePoint Server 2007 (MOSS),它使用 WF 来实现其工作流功能。未来,我们的目标可能是 BizTalk,或决定使用 WF 作为其工作流技术的应用程序。为了演示 WF 和 Pegasus Imaging 的活动如何在为我们宿主 WF 运行时的应用程序中使用,我们将重建第 1 部分中针对 MOSS 的工作流。我们不会深入探讨 WF 和 MOSS 如何工作的全部细节,但我们会略微引起兴趣,以展示 WF 如何使 MOSS 中的工作流定义变得容易。
以下各节假设您已正确配置 MOSS 开发环境。这通常包括在您的开发机器或某个易于访问的位置安装 MOSS。为了使这些步骤成功,项目模板将需要访问 MOSS .NET 程序集——主要是 Microsoft.SharePoint。
项目设置
就像我们在第一部分中做的那样,启动 Visual Studio 2008,并打开**新建项目**对话框。在**Visual C#**标题下,选择**Workflow**作为项目类型,然后选择**SharePoint 2007** **Sequential Workflow**作为项目模板。为您的项目输入一个名称,单击“确定”,项目模板向导将启动。
在 Visual Studio 2008 之前,将 SharePoint 工作流部署到服务器有点麻烦,但新的向导大大简化了此过程。首先,项目模板向导将提示您输入要在 SharePoint 中用于工作流的名称以及用于调试和部署工作流的 SharePoint 服务器。
一旦名称和项目站点建立,模板将询问您是否希望 Visual Studio 自动将工作流与 SharePoint 中的库或列表关联起来。我们不会在这里详细解释 SharePoint 对象模型,但您确实需要知道必须将您的工作流绑定到两者之一。SharePoint 使用此关联在工作流运行时向工作流传递数据。例如,如果您将工作流与文档库关联,工作流将在此库中的特定项目上运行。此外,SharePoint 中的工作流使用历史记录列表记录其操作的详细信息,并跟踪每个工作流参与者的任务,因此您可以在同一对话框中选择要用于工作流的这些选项。
最后,项目模板向导的最后一步将询问您希望如何启动工作流。有三个选项——您可以手动启动它,当在指定列表中创建新项目时启动工作流,或者当项目被修改时启动工作流。为简单起见,我们将让我们的工作流手动启动。
此时,Visual Studio 拥有生成项目所需的一切,因此我们可以单击“完成”,让工具完成其工作,然后开始专注于我们希望工作流做什么。
默认工作流和部署
项目生成后,您拥有构建和部署工作流所需的一切,而无需担心托管 WF 运行时。项目将创建一个默认工作流,以及几个包含 SharePoint 部署信息和用于签署程序集的强名称密钥文件的 XML 文件。第一个 XML 文件 feature.xml 用于通知 SharePoint 系统中正在安装的新功能,并允许我们为此功能设置标题和描述。此文件还包含对 workflow.xml 的引用,其中列出了要安装的工作流的详细信息。正如您可能想象的,此描述中遗漏了大量细节,但幸运的是,我们无需过多担心。向导已为我们处理了这些事情。目前,为了保持简单,我们不会将默认工作流重命名为更具意义的名称。如果重命名工作流,我们将不得不担心在多个地方更新项目,因此目前我们不会混淆问题。
要开始,请通过在解决方案资源管理器中双击 *Workflow1.cs* 来确保 WF 设计器图面可见。当设计器可见时,您会注意到的第一件事是图面上已经存在一个名为 `onWorkflowActivated1` 的 `OnWorkflowActivated` 类型活动。此活动充当 SharePoint 和您的自定义工作流之间的桥梁,并通过其 `WorkflowProperties` 属性提供有关工作流应操作的项目和列表的信息。默认设计器图面如下图 10 所示。
至此,我们有了一个什么都不做但可以完全部署到 SharePoint 网站的工作流。为此,我们只需在 Visual Studio 2008 中按下 Ctrl + F5(或如果您希望在调试器中运行,则按下 F5),然后让工具完成它们的工作。一旦您的工作流部署完毕,系统将导航到您创建项目时指定的网站,以便您可以选择要用作工作流输入的项目。要运行工作流,您需要从项目下拉菜单中选择**工作流**,如下图所示。
单击**工作流**将弹出一个可为此 SharePoint 项目运行的工作流列表。您所需要做的就是单击**ImageCleanerSP**即可运行。当工作流完成后,项目在其库中的视图将显示**ImageCleanerSP**的状态为“已完成”。
在我们继续之前,让我们回顾一下到目前为止我们已经涵盖的步骤。
- 我们首先创建了一个新的**SharePoint 2007 顺序工作流**项目。我们通过向导提供了信息,将我们的工作流与 SharePoint 库或列表关联起来。
- 接下来,我们研究了项目布局,简要检查了 *features.xml* 和 *workflow.xml*,并了解了 `OnWorkflowActivated` 活动的重要性。
- 最后,我们学习了如何使用 F5 部署将工作流部署到 SharePoint 服务器,以及如何手动运行 SharePoint 库或列表中的特定项目的工作流。
正如你所看到的,有很多东西需要理解,但这次,没有代码需要编写——只有一些配置需要完成。但现在这些都完成了,我们进入了有趣的部分。
将图像处理添加到工作流中
为了构建我们的工作流,我们将遵循与第 1 部分中用于命令行应用程序工作流相同的拖放序列。但是,当您将活动添加到设计器表面时,我们必须确保所有活动都添加到 `OnWorkflowActivated` 活动之后。一旦我们将活动添加到表面,它将如下图所示。正如我们在第 1 部分中看到的那样,在设计表面上组织完活动后,我们并没有完成。我们还需要连接每个活动的输入和输出,这与第 1 部分中完全相同,只是略有不同。
如果您还记得第 1 部分,我们在调用 `CreateInstance` 时接收了作为参数的输入文件名和输出文件名。`LoadImage` 和 `SaveImagePDF` 活动绑定到这些参数,以解析每个活动运行所需的 `FileName` 属性。但在我们的 SharePoint 实现中,我们没有命令行应用程序传递参数。相反,我们有来自 `OnWorkflowActivated` 活动的 `WorkflowProperties`,它提供工作流运行所需的信息,包括我们要处理的项目的名称。将 SharePoint 传递的项目转换为 `LoadImage` 活动可以接受的输入需要做一些工作(`LoadImage` 在初始版本中只能从文件加载)。为简洁起见,我们在此不解释细节,但要了解更多信息,请查看本文随附的示例。我们基本上获取 SharePoint 项目,将项目写入我们可以从中加载的临时文件,执行我们的图像处理工作,然后将结果上传回 SharePoint。
一旦添加了额外的活动以将 SharePoint 项目转换为 Pegasus 活动可接受的输入,您的设计器应如下图所示。
要运行我们已完成的工作流,只需使用 F5 部署,选择您的项目,然后像以前一样运行工作流。如果一切正常,您的 SharePoint 库中应该会有一个新文档,该文档已清理(如果是 1 位图像)并转换为 PDF。要继续探索 WF,您可以尝试通过添加一个额外的活动来稍微修改工作流。您所要做的就是将所需的活动拖到表面,连接其输入和输出,然后重新部署。
总结
我们已经涵盖了大量内容,我希望对 WF 的解释以及可以实现的功能示例已经激发您对这项技术的兴趣。最终,WF 的成功程度将取决于开发社区决定如何利用它——通过在其应用程序中托管 WF 运行时并开发领域特定活动。Pegasus Imaging Corporation 已承诺根据市场需求提供图像处理活动,因此请告诉我们您希望看到哪些类型的活动。与此同时,请此处查看我们当前的产品。