(翻译)LearnVSXNow! #9 – 创立大家首先个工具集-重构为劳动起名

上一篇:【翻译】WF从入门到了解(第2章):workflow运维时

    
在第四篇和第九篇里,大家创设了二个名为StartupToolset的言传身教package,并且手动地添加了三个菜单项和工具窗。在那篇小说里,大家将重构那几个package,提取独立的劳务模块出来。

    学习完本章,你将明白:
    1.使用不带参数和带参数二种方法开头化二个workflow实例
    2.测定你运维中的workflow实例的光景
    3.停止workflow实例
    4.鲜明你的workflow空闲或终止的来头

    
大家这一个示例package有很多地点能够重构:不仅能够做提取服务等等的结构调整,也能够打包可选择的代码,以便供以往调用或增加代码可读性。在下一篇小说里大家将打包可选拔的代码,但在这一篇里,我们把精力放在服务上。

    一个workflow实例由一个或五个运动结合。(大家将在第十章早先介绍种种活动:“Basic
Activity Operations.”)“primary activity”或许“root
activity”被称作“workflow definition”。“workflow
definition”日常的作为是为任何将要工作的移位担任八个器皿。
    注:“workflow
definition”是您须求workflow去实施的事物,而3个workflow实例是多个正值执行的“workflow
definition”。它们中间有显著的区分,三个正在实践在那之中,而另二个则不是。
    workflow实例从何地来?它们当然应由你来创立。如您有不便来成功这么些任务,并且自动创立的workflow符合你的选取供给的话,也可由软件来实现,但最少你也要写出workflow的职分依旧workflow运维时将为您执行的职务。Microsoft提供了workflow运营时,你还得制造余下的东西。毕竟,那是你的施用。
    WF在上述那么些地点的开创上得以为您提供支持,WF不仅将举行你创立的workflow实例,而且也将救助您去创立它们。WF集成了拉长的图形界面设计器,它能帮您以同等的办法把workflow集成到你创设的ASP.NET
Web Forms、Windows
Forms大概WPF应用中。你可在工具箱上滚动鼠标,从众多活动项中选中1个,然后把它拖到设计界面上并释放它。如果那个运动项具有可配置的品质,你还可使用Visual
Studio中的属性面板来配置它,使它适合您的企图。大家已在第①章简要地使用过workflow设计器,在此地大家将再也使用它,毕竟与WF相关的劳作差不多全是成立workflow义务,workflow可视化设计器的接纳是开发进程中国和英国豪的一个组成都部队分。

 

    workflow实例和其余其余软件接近。它们会起来实施、运转,直到遇见终止条件时停下。那么些大概是数据库中的全数记录已被处理,全数需被缩减的档案已被压缩,大概workflow发向各种审查批准方的文书档案已被批复(同意或不容许),或然是拍卖已经形成。它唯有2个好端端的起步地点,但有一个或多少个正规的恐怕终止的职分。
    实例也能保持错误、格外。你可以处理那些尤其也能够处理它。在好几情状下,或者你不想去处理出现的丰裕,并留到今后举办拍卖。
    有时,四个workflow处理进度会实行不短相当短日子才能达成。例如,二个处理进度发送了一份零件的订单并等待订单被吸收接纳。在workflow终止前须确认零件的型号和数量,而那恐怕会花去几天,几周甚至几月。因而,难道二个workflow实例也急需在内部存款和储蓄器里维持激活状态几天,几周只怕几月啊?倘若服务器崩溃或电源断电如何做?你的workflow实例、数据、应用程序状态不是清一色丢失了呢?
    workflow实例和整合实例的位移是workflow处理进程中的主要片段。WF已经为workflow实例的创建及举办提供了强压的支撑。大家就来看看WorkflowInstance对象。

复制一份StartupToolset

    
为了在重构此前封存近年来的StartupToolset的版本,小编把这几个package复制了一份,并取名为StartupToolsetRefactored。你能够参考第⑥篇和第七篇的内容自个儿来做1个副本:新建贰个空的名为StartupToolsRefactored的package,并且依照第④篇的始末为它添加二个菜单项,依据第十篇的内容添加三个工具窗。

    
为了制止和前二个package争论,要修改一下StartupToolsRefactored里的GUID,并且修改一下食谱命令的显示文本,那样就足以在界面上和旧版的package区分开来。

    WorkflowInstance对象介绍
    
workflowInstance是多个WF对象,它为您提供了您的独立的workflow职分上下文(环境)。你可选取那个目标去找到在你的拍卖职务山东中华工程公司作将是怎么着进行的。就如大家有点子和性格去决定workflow运转时一致,我们也有主意和属性并用它们和大家的workflow实例举办相互。表3-1列出了大多数WorkflowInstance属性,表3-2列出了常事使用的不二法门。我们还将在第六章看到有些额外的天性和艺术,“工作流跟踪”。
    表3-1 WorkflowInstance的属性

创立1个大局服务(global service)

    
在重构的率先步,大家将把“总计引擎”做成3个大局服务。那样的道别的package就可以调用大家那些服务的功力了。

    
到近日截止,“总结”的逻辑是直接嵌入到大家的工具窗的用户控件CaculationControl类里的。这段逻辑放在了CalculateButton_Click事件处理方法里,那样我们的代码看起来就十分不难并且不难懂。然而在那种组织下,计算逻辑和大家的package是紧耦合的:

public partial class CalculationControl : UserControl

{

  ...

  private void CalculateButton_Click(object sender, EventArgs e)

  {

    try

    {

      int firstArg = Int32.Parse(FirstArgEdit.Text);

      int secondArg = Int32.Parse(SecondArgEdit.Text);

      int result = 0;

      switch (OperatorCombo.Text)

      {

        case "+":

          result = firstArg + secondArg;

          break;

        ...

      }

      ResultEdit.Text = result.ToString();

    }

    catch (SystemException) { ... }

    ...

  }

}

    
最符合的考订方案是把那段计算逻辑放到二个独自的服务目的里。要是大家把那个服务对象做成三个大局的VSX服务以来,不仅大家的CalculationControl控件能够应用它,别的的package也同样能够利用它。OK,就那样做!

属性 功能
InstanceId 得到workflow实例的唯一标识(一个Guid)
WorkflowRuntime 得到本workflow实例的WorkflowRuntime

创办服务接口

    
每1个劳务都不可能不至少提供一个接口来作为劳动的“契约”,所以,不必大惊小怪,我们要创设接口(译者注:从技术上来讲,服务不必然非得要求接口,那点自身在那篇译文的前面会做些测试代码来证实)。大家得以把接口定义在大家的package程序集里,不过,其余package要想用那个服务来说,就不得不引用我们的全部package:大家见怪不怪不想这么做。

    
所以,大家用老配方:创制三个的独立的顺序集来放置服务。那样大家的package和任何的package都足以引用它。

    
成立二个名为StartupToolsetInterfaces的类库项目,并在StartupToolsetRefactored系列里引用它。删除掉暗许的Class1.cs文书,并加上3个CalculationService.cs文件。

    
假使您还记得大家在头里的事例中是怎么着访问到全局服务以来,你早晚会想起来GetService方法:

IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));

    
为获得uiShell那一个服务目的,大家用到了四个连串:IVsUIShell是概念服务的接口;SVsUIShell是所谓的标志类型(markup
type),用它来标识服务对象。你也许会问,大家为什么要用七个项目?只用四个接口类型不就够了吧?是的,三个接口类型就够了,但用多个档次能够增长灵活性:一个服务对象能够兑现1个或三个接口,1个接口也足以被1个或多少个服务对象完结(译者注:例如你有一堆的劳务都是IXXXService类型的,但各样服务的有血有肉达成有所分裂,你就足以定义若干个标志类型来差异这么些分歧的服务)。用四个档次的话,大家即能够给服务对象起名(如SVsUIShell),也得以为服务接口起名(如IVsUIShell)。GetService的参数能够是促成了劳务的品类,但也不必然非得那般。实际上,我们能够传任何类型给它,这么些参数只是用作三个key来标识三个劳务指标。

     标记类型(markup
type)不包括其余功效,它们然则用来标记三个项目,以界别别的系列。

    
我们也服从那种方式来做,在CalculationService.cs文本里,添加七个接口:二个劳务接口和八个标志接口:

using System.Runtime.InteropServices;



namespace StartupToolsetInterfaces

{

  [Guid("D7524CAB-5029-402d-9591-FA0D59BBA0F0")]

  [ComVisible(true)]

  public interface ICalculationService

  {

    bool Calculate(string firstArgText, string secondArgText, 

      string operatortext, out string resultText);

  }



  [Guid("AF7F72EF-2B54-4798-B76A-21DC02CC04B7")]

  public interface SCalculationService

  {

  }

}

    
根据惯例,服务接口以“I”起初,标记接口以“S”伊始。它们必须能够被COM识别,所以要添加Guid。此外,服务接口必须定义为ComVisible,那样非托管代码就足以找寻到它。

    表3-2 WorkflowInstance的方法

创立服务类

    
大家把落到实处总结逻辑的劳务达成类定义在我们的package里(不在StartupToolsetInterfaces类库项目里)。在StartupToolsetRefactored品种里,添加一个CalculationService.cs文件(此CalculationService.cs文本非彼CalculationService.cs
文件),并丰裕类似下边包车型大巴代码:

using System;

using StartupToolsetInterfaces;



namespace MyCompany.StartupToolsetRefactored

{

  public sealed class CalculationService: ICalculationService, SCalculationService

  {

    public bool Calculate(string firstArgText, string secondArgText, 

      string operatorText, out string resultText)

    { ... }

  }

}

   
由于接口是概念在StartupToolsetInterface先后集里的,所以大家要using它们的命名空间。为了能符合规律的创始大家的劳务,服务类必须既贯彻劳务接口,又完成标记接口(markup
type)。倘若你未曾兑现标记接口,编写翻译是没难点的,但那么些服务对象实例是不会被创建的。由于标记类型实际上不带有别的格局,所以大家只须要贯彻Calculate方法就足以了。那些点子的落实能够从CalculationControl控件的CalculateButton_Click艺术里复制过来,并且要做些调整:

public bool Calculate(string firstArgText, string secondArgText, 

  string operatorText, out string resultText)

{

  try

  {

    int firstArg = Int32.Parse(firstArgText);

    int secondArg = Int32.Parse(secondArgText);

    int result = 0;

    switch (operatorText)

    {

      case "+":

        result = firstArg + secondArg;

        break;

      ...

    }

    resultText = result.ToString();

  }

  catch (SystemException)

  {

    resultText = "#Error";

    return false;

  }

  return true;

}

     今后让咱们修改一下CalculateButton_Click措施,来用这几个service:

public partial class CalculationControl : UserControl

{

  ...

  private void CalculateButton_Click(object sender, EventArgs e)

  {

    ICalculationService calcService = new CalculationService();

    string result;

    calcService.Calculate(FirstArgEdit.Text, SecondArgEdit.Text,

                          OperatorCombo.Text, out result);

    ResultEdit.Text = result;

    LogCalculation(FirstArgEdit.Text, SecondArgEdit.Text, OperatorCombo.Text,

      ResultEdit.Text);

  }

  ...

}

 

    
运行StartupToolsetRefactored品类,并且试一下Calculate工具窗,你会意识它可以健康工作。这样就够了呢?不,还不够。以后我们有了服务目的,并且选用了它,但大家还须求告诉VS
IDE这些服务的存在,那样其他package才能用它!

方法 功能
ApplyWorkflowChanges 通过WorkflowChanges对象申请对workflow实例进行更改。这允许你在workflow执行时修改它(增加、移出或更改活动),当动态的更改实施时,workflow实例会被暂停。
GetWorkflowDefinition 检索本workflow实例的根(root)活动。
Resume 恢复执行先前被暂停的workflow实例。假如workflow实例并未处于暂停状态,则不做任何事情。假如workflow实例处在暂停状态,workflow运行时就会在workflow实例刚被恢复后触发WorkflowResumed事件。
Start 启动一个workflow实例的执行,在这个workflow实例根活动上调用ExecuteActivity。假如Start发生异常,它通过调用Terminate终止这个workflow实例,并附加异常相关信息作为终止的原因。
Suspend 同步暂停本workflow实例。假如workflow实例本就处于暂停状态,则不做任何事情。假如workflow实例正在运行,则workflow运行时就暂停该实例,然后设置SuspendOrTerminateInfoProperty(说明原因)并进入Suspend,触发WorkflowSuspended事件。
Terminate 同步终止本workflow实例。当宿主需要终止workflow实例时,workflow运行时就终止这个实例并试图持久化实例的最终状态。然后WorkflowInstance设置SuspendOrTerminateInfoProperty(说明原因)并进入Terminate。最后,它触发WorkflowTerminated事件并把终止原因传到WorkflowTerminateException中的Message属性并包含到WorkflowTerminatedEventArgs事件参数中。另外,假如在持久化时发生异常,workflow运行时取而代之地就把异常传到WorkflowTerminatedEventArgs事件参数中。

提供服务

     在大家使大家的劳务可知和可用在此以前,我们先来看一下VS
IDE中服务体系的编写制定。在第四篇中,笔者讲了须臾间VS
IDE中服务的基本概念,那1回让大家深深部分。

     任何三个对象要是想调用3个劳务以来,它必要求和service
provider“对话”。service
provider达成了IServiceProvider接口,并包含GetService方法:

public interface IServiceProvider

{

  object GetService(Type serviceType);

}

     很简单想象出来:八个service provider包罗了贰个预订义的服务汇集。VS
IDE本人就是二个service provider,然则,VS
IDE能够动态的处理服务指标,因为已安装的package可以提供它们自个儿的劳务给IDE。所以,还应当有3个service
container,service
container达成了IServiceContainer接口,该接口继承自IServiceProvider

public interface IServiceContainer: IServiceProvider

{

  void AddService(...); // --- Overloaded

  void RemoveService(...); // --- Overloaded

}

     AddServiceRemoveService方法提供了大家所期望的service
container的效能。3个VSPackage本人就是二个service
container(当然也是多少个service
provider),因为Package类实现了IServiceContainer

     service container并不是三个平面包车型客车东东,它大概含有parent
container。当添加或移除一个劳动的时候,大家能够把那些服务传给它的parent
container,VS IDE就是用那种布局来治本全局服务的。其余,VS
IDE用SProfferService服务来管理全局服务,但是MPF帮大家遮挡了SProfferService:假设大家的package继承自Package基类的话,大家很少会用到它。

     好了,让我们看看怎么样才能把CalculationService提必要VS
IDE。我们必要做上边包车型客车几步:

  1. 首先步:供给二个主意,该办法负责创建相应类其他劳动对象。
  2. 第叁步:在package上注脚该package能提供的服务类型。
  3. 其三步:为服务指标的始建添加早先化代码。

    还有越来越多和WorkflowInstance相关的情势还未列出。到第4章“实例的加载和卸载”,大家持久化学工业作流到数据库中时将看到她们的愈来愈多细节。

率先步:添加负责创战胜务对象的法门

    
服务指标只会被创制1遍,然后全体的调用方都用那同3个实例。大家得以在package开首化的时候实例化服务对象,也足以在首先个调用者请求这几个服务的时候才去实例化它。

    
在此间大家打算用第两种艺术,所以大家须求二个创战胜务指标的回调方法。在大家的package类中,添加2个CreateService的方法:

private object CreateService(IServiceContainer container, Type serviceType)

{

  if (container != this)

  {

    return null;

  }

  if (typeof(SCalculationService) == serviceType)

  {

    return new CalculationService();

  }

  return null;

}

    
这一个回调方法有八个参数:container是请求那些服务的器皿,serviceType是呼吁的服务类型。尽管能够创建服务来说,该措施必须回到服务实例,不然必须回到null。在上头那一个代码段里,大家只接受是package本人的container,并且不得不创建SCalculationService类型的劳务。

    启航1个工作流实例
    当我们运维一个workflow实例前,我们务必有3个workflow职务让WF去执行。在首先章,大家透过Visual
Studio为大家创立了3个基于workflow的门类,它自动包罗2个workflow任务,大家对它举办了改动以实行美利坚合众国和加拿大邮编的求证。若是须要的话,大家能够回去到那些项目去复制源代码,大概引用PCodeFlow.exe程序集。然后我们就可间接利用这几个已开立的workflow。实际上,你能够如此去做。
    不过,大家照旧应当试着去学会写workflow的施用。让大家经过行使3个饱含延时的一一工作流去模拟1个长日子运作的义务吗。我们将在延时前实施一些代码,以弹出一个音信对话框。在通过延时后,大家将再次弹出一个音讯对话框来指明我们的工作一度收尾。通过本书的上学进程,大家的例证将会越发详细和增加,但近年来我们还处于入门阶段,我们还将维持我们的例子并把注意力越来越多的停放概念上而不是拉长技术上。
    注:记住,顺序工作流执行活动时二个接着2个。那些处理格局可和状态机工作流做下比较,状态机工作流执行活动时是依照状态的变更。假使你今后对此一片茫然的话,不用担心,大家将在下章进入该宗旨。
    
    在WorkflowHost化解方案中添加3个各类工作流项目
    1.初始Visual Studio
贰零零玖,加载上一章创立的名为“WorkflowHost”的消除方案准备展开编写制定。
    2.在缓解方案中添加1个簇新的workflow项目。
    3.品种模板选拔顺序工作流库。
    4.项目名称起名为:LongRunningWorkflow。
    未来开拓workflow的视图设计器准备开创我们的workflow任务。在视图设计器中的大图片中,我们将助长征三号个运动到那几个新workflow职责中:五个Code活动和一个Delay活动。Delay活动将被放到八个Code活动中间,指标是可让大家在Delay执行前和实践后都将弹出2个音讯对话框。最初大家会内定1个相宜的延时时间值,但稍后我们将对workflow职分进展改动,以使workflow职分起始化时能经受大家尤其钦命的一个延时光阴值。

第3步:注解能提供的劳动

    
就像菜单命令和工具窗这样,大家务必在package这里附加二个attribute,以宣称该package能提供的劳动:

[ProvideService(typeof(SCalculationService))] 

public sealed class StartupToolsetRefactoredPackage : Package { ... }

     
ProvideService
属性的效益是:regpkg.exe利用那些attribute去登记我们的劳动,并使我们的package能够按需加载(在率先次调用package的劳务的时候,如若package没有加载,则加载package)。

    
每种服务私下认可以种类的名字作为劳务名,当然也能够透过安装那个attribute的ServiceName属性来改变服务名。

    成立那一个宪章需实践相当长日子的相继工作流 
    1.激活workflow视图设计器,移动鼠标到工具箱中。
    2.从工具箱中甄选Code活动,并把该零件拖拽到workflow设计器的外表。
起名 1
    3.释放鼠标并让Code活动组件落到该各种工作流中。
    4.就像是在首先章一样,大家将拉长一些代码到Code活动中以使worflow职责经过这几个活动时举行。在此单击Code活动以担保该运动的性质面板已被激活。
    5.在性质面板中激活ExecuteCode属性的下拉编辑框,它将同意你命宿将被触发的事件,该事件在Code活动中的代码执行时接触。
    6.输入“PreDelayMessage”。这样就添加了贰个风浪到worflow代码中。稍候,大家将修改那段代码以体现3个信息对话框。但未来大家仍卫冕在workflow的视图设计器上行事,因为大家须求添加其余四个移动。
起名 2
    7.从工具箱中选拔Delay活动并添加到Code活动的底下。
起名 3
    注:顺序活动,就好像我们前些天所做的办事一样,是以一一的办法实行活动。顺序由workflow视图设计器中活动的地方决定。在workflow设计器窗口的顶部的移动首先实施,对于其它活动的履行顺序则按到视图设计器窗口尾部的走向(箭头)决定。在下章大家还将重蹈这一经过。
    8.我们须求为大家的Delay活动建立三个延时时刻值。为此,大家要在Visual
Studio属性面板中改变TimeoutDuration的品质。把最终四个“00”改为“10”,意思是Delay活动将拭目以俟10分钟才允许workflow继续下一步的处理。
    9.现行我们必要足够首个Code活动来展现第四个音信对话框。为此,重复步骤2和手续6添加3个新的Code活动,但设置ExecuteCode的性子为“PostDelayMessage”来作为事件的命名。以下是在workflow视图设计器中浮现的workflow的末尾结果:
起名 4
    大家还剩余四个职务未形成。最后,大家须要把我们的workflow程序集引入到大家的主应用程序中以便执行它。但第2,我们亟须抬高须求的代码以突显那五个音讯对话框。大家曾经在大家的workflow代码成立了四个事件:PreDelayMessage和PostDelayMessage。大家将为它们增进事件处理代码,在里头其实正是弹出音讯对话框的代码。

其三步:添加开头化代码

    
我们的package通过ProvideServiceAttribute使外围的事件知道它的服务的留存,不过为了服务实例能被创造,大家还得抬高级中学一年级些发轫化代码才行。那段代码最棒放在package的构造函数里:

public sealed class StartupToolsetRefactoredPackage : Package

{

  public StartupToolsetRefactoredPackage()

  {

    IServiceContainer serviceContainer = this;

    ServiceCreatorCallback creationCallback = CreateService;

    serviceContainer.AddService(typeof(SCalculationService), 

      creationCallback, true);

  }

  ...

}

    
Package类呈现地完成了IServiceContainer接口,是不曾当面包车型地铁AddService办法的,所以大家务必把this转换来IServiceContainer类型的指标。AddService方法有为数不少重载,大家用在这之中的承受二个参数的不胜:要增加的劳动的类型、当服务第三遍调用时会被调用的回调方法、以及是或不是把这些服务传递给parent
container的标记。大家把最后三个参数设成true,那样就能够保险我们的劳务能够被全局访问。

    为延时前和延时后的风浪添加代码 
    1.单击LongRunningWorkflow项目中的Workflow1.cs文本,查看其代码。
    2.添加“System.Windows.Forms”的引用,并在Workflow1.cs文件宣称以下名称空间:
        using System.Windows.Forms;
    3.定位到新插入的PreDelayMessage方法,在艺术中插入以下代码:
        Message博克斯.Show(“正在进行延时前的代码。”);
    4.和上一步类似,定位到新插入的PostDelayMessage方法,在措施中插入以下代码:
        MessageBox.Show(“正在实施延时后的代码。”);
    借使你那时编写翻译那几个消除方案,那没有别的错误,不过WorkflowHost应用程序依旧会像此前一章一样挂起。为何吗?因为固然我们创造了3个大家能够采纳的workflow程序集,但我们并未请求主应用程序去执行它。WorkflowCompleted事件没有被触发,因而活动重置事件也就不会放出应用程序主线程。
    为施行我们的workflow职责,我们要引用大家新创设的workflow程序集并累加代码,以使WorkflowRuntime对象来爆料workflow职分工作的原初。大家后日就起来吧。

利用服务

   
现在,全数别的package都足以用松耦合的法子来行使大家的盘算服务了。不过大家在CalculationButton_Click方法里是直接实例化它的:

ICalculationService calcService = new CalculationService();

string result;

calcService.Calculate(FirstArgEdit.Text, SecondArgEdit.Text,

                      OperatorCombo.Text, out result);

    大家最佳修改一下它,以便从IDE里得到服务实例:

private void CalculateButton_Click(object sender, EventArgs e)

{

  ICalculationService calcService = 

    Package.GetGlobalService(typeof (SCalculationService)) as ICalculationService;

  if (calcService != null)

  {

    string result;

    calcService.Calculate(FirstArgEdit.Text, SecondArgEdit.Text,

                          OperatorCombo.Text, out result);

    ResultEdit.Text = result;

  }

}

 

    宿主一个自定义workflow程序集并运行三个不带参数的workflow实例
    1.首先在类型WorkflowHost中添加对项目LongRunningWorkflow的引用。
    2.假诺大家明天去编写翻译那个应用程序,WorkflowHost将编写翻译战败。为啥吗?原因是在前一章大家创设WorkflowHost项目时,大家只是添加了亟须的引用以支撑即时的条件编写翻译通过。但现行反革命,我们添加了二个“System.Workflow.Runtime”引用。同时又引入了二个现成的workflow程序集到我们的宿主应用程序中,由此大家供给为WorkflowHost项目添加越来越多的和workflow相关的引用。大家必要添加的引用有“System.Workflow.Activities”和“System.Workflow.ComponentModel”。
起名 5
    3.打开Program.cs文件并一定到Main方法内的上边代码上:
        Console.WriteLine(“等待workflow完成。”);
    4.在上述代码下边添加以下代码:

部分测验

    
到近年来截止,我们的package已经应用了我们创建的服务了。接下来,小编建议你对代码做些近来的更改,并看望我们的package会有怎么着变化。

    
为了能够知情地见到那个生成,小编提出您在CalculateButton_Click情势的最下边调用LogCalculationToOutput主意,那样就可以看看大家的package在执行的时候输出来的调试音讯:

private void CalculateButton_Click(object sender, EventArgs e)

{

  ...

  LogCalculationToOutput(FirstArgEdit.Text, SecondArgEdit.Text, 

    OperatorCombo.Text, ResultEdit.Text);

}

    
大家将对代码做些小的更动,并且每一遍变更都会使大家的劳务不可用:当大家必要取得那个服务的实例的时候,大家只好获得空引用。在这几个历程中不会有别的错误提醒,不过在output窗口里,大家能够发现那些服务不会健康干活。例如,要是大家想总括“1+2”,大家期望在output窗口中能看到“1

  • 2 = 3”,不过大家不得不见到“1 + 2 = ”。

    
作者强烈提出你做这一须臾间那一个改动,并检查改动后的结果,因为劳动开发者平日会犯类似的一无所长,并且不明了错在哪了。所以,求你了,做一下底下的考查(每便试验完要记得“undo”那3次的修改)。

起名 6WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1));
起名 7instance.Start();

试验1:在CalculationService类注脚那里,注释掉对SCalculationService接口的兑现

public sealed class CalculationService: ICalculationService 

  // , SCalculationService

{

  public bool Calculate(string firstArgText, string secondArgText, 

    string operatorText, out string resultText)

  { ... }

}

    
package照样能够编写翻译通过,不过那一个服务对象是迫于被创建的。因为当大家调用GetService主意的时候,那些法子认为重回的劳动目的能够转换来参数里指定的门类。在我们的例证中我们是因而GetService(typeof(SCalculationService))调用的,但回到的CalculationService类的实例是不可见转换到SCalculationService种类的,因为它并不曾兑现SCalculationService接口。

    5.编写翻译并执行WorkflowHost应用程序。
    执行结果如下:
起名 8
    让大家回来下边相当首要的代码上:

测验2:在调用Add瑟维斯方法时,把最后一个参数从true改成false

public StartupToolsetRefactoredPackage()

{

  IServiceContainer serviceContainer = this;

  ServiceCreatorCallback creationCallback = CreateService;

  serviceContainer.AddService(typeof(SCalculationService), creationCallback,

    false);

}

    
那样改后,大家也得不到服务的实例了,那是因为Package.GetGlobalService主意找的是负有公开给VS
IDE的劳动,可是我们把AddService的终极一个参数改成false之后,我们的劳务就不是开诚相见的了。

起名 9WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1));
起名 10instance.Start();

用本地的主意选取劳务

    
到近期甘休大家都以透过调用Package.GetGlobalService主意来获得服务实例的,看起来像是这几个服务是其他package而不是大家的package提供的。其实,大家得以用GetService方法:

private void CalculateButton_Click(object sender, EventArgs e)

{

  ICalculationService calcService = 

    GetService(typeof (SCalculationService)) as ICalculationService;

  ...

}

    
那样改动后,大家的package照样运维平常化!可是这么些GetService方法是从哪个地方来的吧?CalculateControl用户控件和大家的package没有间接的关联,它继续自UserControl类,UserControl又一而再自System.ComponentModel.Component,而以此类完毕了IServiceProvider接口,还记得不,这么些接口定义了GetService主意!可是,属于用户控件的GetService方法是怎么知道大家的package会提供这几个服务的?大家并从未在这几个用户控件里一贯引用package啊。

     原因正是VS
IDE的Siting机制。当大家的package加载到IDE的时候,它被site了,并且获得了四个parent
IServiceProvider;当大家的工具窗里的用户控件加载到内部存款和储蓄器的时候,那一个控件也被site到工具窗中,所以也会有一个parent
IServiceProvider,这多个service
provider是同1个对象。用户控件的GetService方法在实践的时候,会寻找整个IServiceProvider链。在这一个链中,它会调用到我们的package的GetService方法并最后收获这几个服务目的。这是一种本地访问服务的不二法门。就算注释掉package上附加的ProvideService质量的话(译者注:仅注释掉是不够的,要卸载package然后再登记),大家的package也足以正常运作,不过这一个服务就不再是三个大局服务了,其他package不一定能够再利用它。(译者注:在别的package请求这几个服务时,没办法知晓这几个服务在哪个package内,所以也就不或者选拔那一个服务,可是只要我们的package已经加载了,那么别的package还是得以获得这几个服务,因为在大家package的构造函数里,大家把那么些服务加到了parent
service container里)

    在那里,我们应用了WorkflowRuntime对象的CreateWorkflow方法创立了二个大家想去执行的workflow实例。当大家赢得了二个回去的WorkflowInstance对象后,我们调用了它的Start方法来发轫化workflow。注意这一个workflow实例不必要大家先行输入参数就能进行。假如能预先输入三个可变的延时值那该多好?下一节咱们将探讨这一话题。 

 

    起始1个带参数的workflow实例 
    带输入参数运维的Workflow实例把收到到的参数和血脉相通的国有属性对应起来。正是说,为流传2个可变的延时值,大家只需在大家的workflow实例上开创五个国有的名为“Delay”属性,并在创造那个实例时提供延时值即可。倘若你对XML体系化和.NET中的“XmlSerializer”熟知的话,成立一个workflow实例的历程就和把XML流反类别化成二个.NET目的的历程相似。事实上,那大概差不离。
    期望被传播workflow实例的参数值常常存款和储蓄在二个Dictionary对象的Values中,Dictionary对象的要害字接纳string类型,对应的值使用简便的Object对象。典型的代码如下:
        Dictionary parms = new Dictionary();
    然后您可利用Dictionary对象的Add方法添加参数。关键字必须是一个string,它意味着workflow的root活动所揭露的国有属性的名号。别的,对应的值的类型必须和移动的习性类型一致。例如,大家传入三个整形类型的延时值并且我们的workflow实例中揭示了八个名为Delay的属性和其对应,那添加一个参数到Dictionary中的代码就应像下边包车型大巴同一:
        parms.Add(“Delay”,10); //延时10秒。
    大家重新来写一些代码吧,大家相对做些小的修改,但那会赢得广大作用。大家以控制台命令行的法门接受大家输入的三个整形数值作为延时值。为使大家的顺序不会永远运转下去,大家会把那么些值限制在0到120中间,意思延时范围从0秒到两分钟之间。大家也将对workflow增添Delay属性。让咱们一起来对我们的workflow组件做第③遍改正。
    为workflow添加二个输入属性
    1.开拓Workflow1.cs文书准备编辑。
    2.在Workflow1的构造函数后,添加以下代码:

总结

    
原来的StartupToolset里的乘除逻辑是耦合在工具窗的用户控件里的,在这篇小说里,大家把那段逻辑抽了出去做成了3个大局服务。为成立这几个服务,我们在一个独立的顺序集里添加了多个接口:

  1. 服务接口注脚了服务的职能(契约)。
  2. 标记类型(无成员的接口)被视作GetService的参数。

    
在package项目中,大家添加了3个劳务完结类,完毕了劳动接口和符号接口,并商量了服务的机制和使劳动能被全局访问的步骤。我们的劳动实例在首先次被呼吁时才会创制。其它,我们还明白了怎样以大局和地方的措施来走访服务。

     在下一篇里,大家一而再重构这几个package,并创办可采取的代码。

 

初稿链接:http://dotneteers.net/blogs/divedeeper/archive/2008/01/31/LearnVSXNow9.aspx

 

     到此处那篇译文就已经截至了,但自个儿还想再多表明有个别事物:

起名 11private Int32 _delay = 10;
起名 12
起名 13public Int32 Delay
起名 14起名 15起名 16{
起名 17起名 18    get 起名 19{ return _delay; }
起名 20    set 
起名 21起名 22    起名 23{
起名 24        if (value < 0 || value > 120)
起名 25            value = 10;
起名 26        if (ExecutionStatus == ActivityExecutionStatus.Initialized)
起名 27起名 28        起名 29{
起名 30            _delay = value;
起名 31            delayActivity1.TimeoutDuration = new TimeSpan(0, 0, _delay);
起名 32        }
起名 33    }
起名 34}

1。服务一定需求定义成接口吗?

    
如果仅仅从技术上来看,服务不必然非得要求接口。为了表明那一点,我们在StartupToolsetInterfaces项目里添加2个My瑟维斯Class.cs文件,并丰裕如下代码:

public class MyServiceClass

{

    public int Caculate(int i)

    {

        return i*i;

    }

}

    
然后用那篇译文里的点子添加ProvideService、回调方法,并在package的构造函数里调用AddService。然后,新建三个带菜单项的package,并加上对StartupToolsetInterfaces的引用,然后在菜单项的事件处理方法里,添加如下代码:

MyServiceClass myService = GetService(typeof(MyServiceClass)) as MyServiceClass;

if (myService != null)

{

    MessageBox.Show(myService.Caculate(3).ToString());

}

    
运维起来后,点击那几个package的菜系,是或不是弹出了一个音信框,并体现9?所以,服务不肯定非得用接口,但用接口会更好,能够使协会更好,又或然能够使非托管代码可以访问那些服务(笔者并没有证实过)。

 

    大家对传播的整形值进行了自小编批评,假设它超出范围,大家就钦命贰个私下认可值。大家检查了workflow是或不是处于即将执市价况(不是已执市价况)。那可防患有人在咱们的workflow运行中对延时值进行改动。我们也须要对Main方法举行少量改动。大家需在命令行中输入3个参数作为延时值。假使它不是贰个整形值,大家就淡出。不然,我们就承受它。假诺它超越限制(0到120秒),大家就对它进行供给的约束(为暗中认可值10秒)。对Main所做的改动步骤如下:
    启航叁个带参数的workflow实例     1.打开Progrom.cs文件准备编辑。
    2.稳住到上面包车型客车代码上:

2。服务的GUID是为啥用的?

    
在上边这几个示例服务MyService里,大家并不曾给她加GUID,但原著笔者给出的例证却加了GUID,那么那一个GUID是干嘛用的吗?其实,GUID无非就是标识这一个服务而已。然后打开注册表,在HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0Exp\Configuration\Services上面就足以找到这一个GUID,是ProvideService那几个attribute钦赐的SCalculationService接口的GUID。假使没有给它加GUID,regpkg在注册的时候,会活动发出3个GUID,所以,一般景色下也不用给劳务钦定GUID。

    
但在一些情状下,那么些GUID依旧实惠的。比如由于某种原因,大家的package不能引用StartupToolsetInterfaces项目,不过在package里又想用它的service,大家就足以在package项目里加一个接口或类(该接口或类能够是空的),然后给她一个GUID,GUID的值是StartupToolsetInterfaces里的SCalculationService的GUID:

[Guid("AF7F72EF-2B54-4798-B76A-21DC02CC04B7")]

class MyService

{

 //空的   

}

    
然后把团结定义的这些接口类型传给GetService方法,那样就还能够博得那么些服务的实例(是叁个object类型的),然后通过反射来调用它的措施了。

 

object service = GetService(typeof(MyService));

if(service != null)

{

    //反射调用其方法

}

    
当然,通过反射来调用看起来很怪,应该有任何方法得以用“强类型”的点子使用这些服务,例如像使用COM对象那样,定义interop类型,但自小编缺乏那上头的文化,所以并未去印证如何使用。

起名 35workflowRuntime.WorkflowIdled += new EventHandler<WorkflowEventArgs>(workflowRuntime_WorkflowIdled);
起名 36workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted);
起名 37workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(workflowRuntime_WorkflowTerminated);

    3.在上述代码后添加下边包车型客车代码:

起名 38Int32 delay = 0;
起名 39string val = args.Length > 0 ? args[0] : “10”;
起名 40if (!Int32.TryParse(val, out delay))
起名 41起名 42起名 43{
起名 44    Console.WriteLine(“你必须输入3个整形值!”);
起名 45    return;
起名 46}
起名 47
起名 48Dictionary<string, object> parms = new Dictionary<string, object>();

    4.找到下边的代码:

起名 49WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1));

    5.把上述代码改为:

起名 50WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1), parms);

    编写翻译并像在首先章那样运维试试,通过输入不相同数值的延时时间,就可知到弹出的七个音信对话框所显示出的延时效果。

    明确Workflow实例的情形 
    有趣的是,倘若你看看workflow运转时对象及workflow实例对象的办法和品质,你找不到和景色相关的质量。你怎么知道是或不是有贰个workflow在进行呢?假设有一个,它地处相当状态呢?空闲吗?正推行个中吗?大家怎么规定?
    小编将向前跳一小段,这么些中绝当先6/10逻辑都坐落workflow状态的明确上。2个加以的workflow实例的workflow
definition为您提供workflow的推市场价格况。基类Activity揭发了叁个ExecutionStatus属性,它是1个ActivityExecutionStatus枚举的三个成员。下表3-3列出了ActivityExecutionStatus的枚举值和有关的意思。
表3-3 ActivityExecutionStatus枚举值

属性 功能
Canceling 活动正在取消中。
Closed 活动已被关闭。
Compensating 活动处于补偿状态。
Executing 活动当前正在运行。
Faulting 活动已产生并维持一个异常。
Initialized 活动已被初始化但还未运行。

    表3-3中的全部枚举值都涉嫌到二个运动目的,但您需记住workflow
definition正是一个活动。那意味着假使大家询问workflow
definition的图景,我们就能管用地规定整个实例的情形。下边包车型地铁经过演示了大家什么添加相应代码来查询workflow
definitely。

    鲜明workflow实例执市场价格况
    1.开拓WorkflowHost项目标Program.cs文件准备编辑。
    2.找到Main方法并平素到上边包车型地铁代码上:
        instance.Start();
    3.为了让大家来看workflow实例的情事,我们从来询问workflow
definition的事态并把结果输出到控制布里斯托显示出来。在上一步中一定到的代码下插入以下代码:
        Console.WriteLine(“workflow处在:{0}状态。”,
            instance.GetWorkflowDefiniton().ExecutionStatus.ToString());

    终止Workflow实例
    若是你必要那样做的话,你也能便于地平息3个workflow实例,方法是透过履行workflow实例对象的Terminate方法。假使你在你的使用中添加了WorkflowTerminated的事件处理,你就能从Exception的Message属性获取终止的由来。你将发现Exception被卷入到WorkflowTerminated伊芙ntArgs中,并传播到WorkflowTerminated的事件处理程序中。那个代码WorkflowHost中曾经包括了,我们还需添加一行代码来了却workflow实例。
    终止workflow实例
    1.开拓Program.cs文件,找到如下大家刚添加的代码上:
        Console.WriteLine(“workflow处在:{0}状态。”,
            instance.GetWorkflowDefinition().ExecutionStatus.ToString();
    2.在上述代码下添加以下代码:
        instance.Terminate(“用户废除”);
    要是你将来编写翻译并运转WorkflowHost程序,为他提供3个25秒的延时值,你不会再见到任何三个信息对话框,控制台的出口结果如下:
起名 51

    Dehydration和Rehydration 
    在我们距离workflow实例的这一话题此前,小编想再谈谈“dehydrating”和“rehydrating”三个实例的定义。即便你有二个长日子运作的workflow任务依然有大气的天职履行,你就能卸载任务并把必须的施行环境音信存款和储蓄到3个SQL
Server数据库中,这要用到运维在WF之上的贰个劳务。
    我们将在第四章详细谈论存款和储蓄的题材,作者在那提及它是因为,对一件事来说,处理的靶子是workflow实例。但另一方面,大家应听取那个术语,作者不想让你在深切此书后却还不知道它们的主导意思。
    当你“dehydrate”一个实例时,你就正在把它从推市场价格况中移除并展开仓储以便以往苏醒。典型的做法是使用WF的持久化服务,但你也能写你本人的劳务来做一样的任务。现在当你的应用程序侦测到需重启workflow实例时,你就“rehydrate”那几个实例它就回去当时的实施景况。那样做的因由有很多,全数那个本书稍后都会简单表明。

    源码下载:WorkflowHost

下一篇:【翻译】WF从入门到驾驭(第⑥章):活动及workflow类型介绍

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图