本文章是由機器翻譯。

基礎架構

本機通訊的工作流程服務

Matt Milner

下載程式碼範例

前一欄 (請參閱 「 工作流程通訊 」] 在 2007 年九月發行的 MSDN Magazinemsdn.microsoft.com/magazine/cc163365.aspx),我撰寫關於核心通訊架構,在 [Windows 工作流程基礎 3 (WF3)。 我並不涵蓋的主題是本機通訊活動,是一個抽象概念,這個通訊架構的頂端。 如果您查看.NET Framework 4 Beta 1,您會注意到沒有 HandleExternalEvent 活動。 在實際上 WF4,與包含的通訊活動會建置 Windows Communication Foundation (WCF)。 這個月我顯示如何使用 WCF 工作流程和主機應用程式在 Windows 工作流程基礎 3 之間的通訊。 取得這項知識,應該幫助您開發的努力使用 WF3 並準備 WCF 其中透過隨附架構 (稱為 「 書籤 」 中 WF4) 佇列是唯一的抽象概念的 WF4。 (如 WF3 中的工作流程服務的基本資訊,請參閱我出刊] 欄中的 MSDN Magazine, msdn.microsoft.com/magazine/cc164251.aspx 在 Visual Studio 2008 啟動問題)。

概觀

主應用程式和工作流程之間的通訊證明挑戰有些開發人員,因為他們可以輕易地忽略了工作流程和主應用程式經常執行不同執行緒的事實。 通訊架構的設計被為了保護開發人員從不用擔心管理執行緒內容封送處理資料和其他低階詳細資料。 透過在 WF 中佇列的架構的一個抽象概念是引進.NET Framework 版本 3.5 將 WCF 訊息整合。 大部分範例和實驗室會顯示如何將公開給外部裝載處理序的用戶端的工作流程使用活動及 WCF 的擴充功能,但是這個相同的通訊架構可以用來在相同的處理序內進行通訊。

實作通訊包括幾個步驟,但工作不會不金額以大部分多於您會有何與本機通訊活動。

您可以執行其他動作之前,您需要定義 (或以最簡化的方式開始定義在反覆執行的處理方式) 使用 WCF 服務合約的通訊協定。 接下來,您需要在您的工作流程中使用那些合約來建立通訊點邏輯中的模型。 最後,一起虎,工作流程和其他服務必須具有設定端點,為 WCF 服務裝載。

模型化通訊

模型化通訊之第一個步驟是定義您的主機應用程式和工作流程之間合約。 WCF 服務使用合約定義組成服務和訊息,傳送及接收的操作的集合。 在這種情況下因為您正通訊從主應用程式工作流程和工作流程主機,您需要定義兩個服務合約和相關的資料合約,如 的 圖 1 所示。

圖 1 的通訊協定

[ServiceContract(
    Namespace = "urn:MSDN/Foundations/LocalCommunications/WCF")]
public interface IHostInterface
{
[OperationContract]
void OrderStatusChange(Order order, string newStatus, string oldStatus);
}

[ServiceContract(
    Namespace="urn:MSDN/Foundations/LocalCommunications/WCF")]
public interface IWorkflowInterface
{
    [OperationContract]
    void SubmitOrder(Order newOrder);

    [OperationContract]
    bool UpdateOrder(Order updatedOrder);
}

[DataContract]
public class Order
{
    [DataMember]
    public int OrderID { get; set; }
    [DataMember]
    public string CustomerName { get; set; }
    [DataMember]
    public double OrderTotal { get; set; }
    [DataMember]
    public string OrderStatus { get; set; }
    }

使用就地合約,模型使用 [傳送及接收活動的工作流程運作進行遠端通訊一樣。 那是關於 WCF 美麗的事情:遠端或本機,程式撰寫模型是一樣的。 簡單的範例 的 圖 2 顯示具有兩個接收活動和一個傳送活動模型的工作流程和主應用程式之間通訊的工作流程。 接收活動被設定成 IWorkflowInterface 服務合約,並傳送活動使用 IHostInterface 合約。

因此遠本機通訊使用 WCF 不多了不同遠端通訊使用 WCF,且非常類似於使用本機通訊活動和服務。 如何撰寫主機程式碼來啟動工作流程和處理來自工作流程通訊進來的主要差異。

圖 2 工作流程模式化依據合約

裝載該服務

因為我們要讓通訊流程使用 WCF 這兩種方式,我們需要裝載兩個服務 — 工作流程服務,才能在主應用程式來接收郵件從工作流程中執行工作流程和服務。 在我的範例我建置一個簡單的 Windows Presentation Foundation (WPF) 應用程式做為主機,而且用於應用程式類別 ’s OnStartup 和 OnExit 方法管理該主機。 第一個 inclination 可能建立 WorkflowServiceHost 類別並開啟 [右 OnStartup 方法中。 由於之後主應用程式已開啟,Open 方法並不會封鎖,您就可以繼續處理、 載入使用者介面,並開始與工作流程互動。 WPF (與其他用戶端的技術) 使用單一執行緒的方式進行處理,因為這會很快就導致問題因為服務與用戶端呼叫不能使用相同的執行緒讓用戶端逾時。 若要避免這個問題,[WorkflowServiceHost 上會建立另一個執行緒使用 [ThreadPool 所示 圖 3

圖 3 裝載工作流程服務

ThreadPool.QueueUserWorkItem((o) =>
{

//host the workflow
workflowHost = new WorkflowServiceHost(typeof(
    WorkflowsAndActivities.OrderWorkflow));
workflowHost.AddServiceEndpoint(
    "Contracts.IWorkflowInterface", LocalBinding, WFAddress);
try
{
    workflowHost.Open();
}
catch (Exception ex)
{
    workflowHost.Abort();
    MessageBox.Show(String.Format(
        "There was an error hosting the workflow as a service: {0}",
    ex.Message));
}
});

您會遇到的下一個挑戰選擇本機通訊適當的繫結。 目前,沒有沒有在記憶體,或者同處理序繫結,是極為輕量的這類的案例。 輕量級通道最佳的選項是使用 [NetNamedPipeBinding 以安全性已關閉。 不幸的是,如果嘗試使用此繫結和主機工作流程服務作為您會得到通知主機需要與存在的內容通道的繫結,因為服務合約可能需要在工作階段錯誤。 進一步,是隨附在.NET Framework 其中只有三個內容繫結的船隻沒有 NetNamedPipeContextBinding:BasicHttpContextBinding、 NetTcpContextBinding 和 WSHttpContextBinding。 幸運的是,您可以建立自己自訂的繫結,以包含內容通道。 圖 4 顯示自訂的繫結,衍生自 NetNamedPipeBinding 類別,並會在 ContextBindingElement 插入到繫結。 現在兩個方向通訊就可以這種繫結使用端點註冊資料中藉由使用不同的地址。

圖 4 NetNamedPipeContextBinding

public class NetNamedPipeContextBinding : NetNamedPipeBinding
{
    public NetNamedPipeContextBinding() : base(){}

    public NetNamedPipeContextBinding(
        NetNamedPipeSecurityMode securityMode):
        base(securityMode) {}

    public NetNamedPipeContextBinding(string configurationName) :
        base(configurationName) {}

    public override BindingElementCollection CreateBindingElements()
    {
        BindingElementCollection baseElements = base.CreateBindingElements();
        baseElements.Insert(0, new ContextBindingElement(
            ProtectionLevel.EncryptAndSign,
            ContextExchangeMechanism.ContextSoapHeader));

        return baseElements;
    }
}

與這個新的繫結可以在 [WorkflowServiceHost 上建立端點,並開啟主應用程式有沒有更多的錯誤。 工作流程已準備好要接收主機使用服務合約中的資料。 該將資料的傳送您需要建立一個 Proxy,並叫用作業如 的 [圖 5] 所示。

圖 5 的 主機程式碼] 以開始一個工作流程

App a = (App)Application.Current;
    IWorkflowInterface proxy = new ChannelFactory<IWorkflowInterface>(
    a.LocalBinding, a.WFAddress).CreateChannel();

    proxy.SubmitOrder(
        new Order
        {
            CustomerName = "Matt",
            OrderID = 0,
            OrderTotal = 250.00
        });

由於共用合約,有是沒有 Proxy] 類別,所以您不必使用 ChannelFactory < TChannel > 建立用戶端 Proxy。

工作流程時裝載並準備好接收訊息,它仍然必須設定來傳送訊息給主應用程式。 最重要工作流程必須可以使用傳送活動時,取得用戶端端點。 傳送活動可讓您指定端點名稱通常是在組態檔中的具名端點的對應。 雖然將端點資訊放在組態檔中運作,但您也可以使用 ChannelManagerService (如在 msdn.microsoft.com/magazine/cc721606.aspx 我 2008 年八月的專欄中討論) 來保留您傳送的活動工作流程中所使用的用戶端端點。 圖 6 顯示建立服務、 提供一個具名端點,並將其新增至裝載於 [WorkflowServiceHost WorkflowRuntime 控管的程式碼。

圖 6 加入至執行階段 ChannelManagerService

ServiceEndpoint endpoint = new ServiceEndpoint
(
    ContractDescription.GetContract(typeof(Contracts.IHostInterface)),
        LocalBinding, new EndpointAddress(HostAddress)
);
endpoint.Name = "HostEndpoint";

WorkflowRuntime runtime =
    workflowHost.Description.Behaviors.Find<WorkflowRuntimeBehavior>().
WorkflowRuntime;

ChannelManagerService chanMan =
    new ChannelManagerService(
        new List<ServiceEndpoint>
        {
            endpoint
        });

runtime.AddService(chanMan);

有裝載工作流程服務可讓您從至工作流程主機傳送郵件,但訊息取回主應用程式,您必須可以從工作流程接收訊息的 WCF 服務。 此服務是標準的 WCF 服務主控應用程式中。 因為服務不是工作流程服務,您可以使用標準 NetNamedPipeBinding 或重複使用先前顯示 NetNamedPipeContextBinding。 最後,因為這項服務會從工作流程叫用,它可以裝載在進行與 UI 項目之間的互動更容易在 UI 執行緒上。 圖 7 顯示服務的控管的程式碼。

圖 7 裝載主機服務

ServiceHost appHost = new ServiceHost(new HostService());
appHost.AddServiceEndpoint("Contracts.IHostInterface",
LocalBinding, HostAddress);

try
{
    appHost.Open();
}
catch (Exception ex)
{
    appHost.Abort();
    MessageBox.Show(String.Format(
        "There was an error hosting the local service: {0}",
    ex.Message));
}

裝載的兩個服務您可以現在執行工作流程、 將訊息傳送和接收訊息回。 但是,如果您嘗試傳送使用第二個此程式碼的第二個訊息接收工作流程中的活動,您會收到錯誤訊息的相關內容。

處理執行個體相互關聯

處理內容問題的一種方法是使用相同的用戶端 Proxy 服務的每個引動過程。 這可讓用戶端 Proxy 管理內容識別項 (使用 [NetNamedPipeContextBinding),並將它們傳送回至後續要求服務。

在某些情況下它 ’s 不可能會保持相同 Proxy 周圍的所有要求。 請考慮大小寫啟動工作流程、 保存到資料庫及關閉用戶端應用程式位置。 用戶端應用程式啟動時再次,您會需要一個方法恢復工作流程,傳送其他郵件給該特定執行個體。 其他常見使用案例是當您要使用單一用戶端 Proxy,但需要幾個工作流程執行個體,每個都有唯一的識別項與互動。 比方說使用者介面提供的每個都有對應的工作流程的訂單清單,當使用者叫用選取的訂單上一個動作,您需要將訊息傳送至工作流程執行個體。 讓管理內容識別項繫結將無法作用在這種情況下,因為它將永遠使用與其互動的最後一個工作流程的識別項。

第一個案例 — 每個呼叫使用新的 Proxy — 您需要手動設定工作流程識別碼到內容中使用 IContextManager 介面。 IContextManager 存取透過 GetProperty < TProperty > 方法 IClientChannel 介面上。 一旦將 IContextManager 您可以使用它來取得或設定內容。

內容本身是最重要的是 instanceId 值名稱-值組的字典。 下列程式碼將示範如何您擷取識別碼從內容讓它可以由儲存您的用戶端應用程式,為稍後,當您需要與相同的工作流程執行個體互動。 在這個範例將識別碼正在顯示在用戶端使用者介面,而不是儲存在資料庫中:

IContextManager mgr = ((IClientChannel)proxy).GetProperty<IContextManager>();
      
string wfID = mgr.GetContext()["instanceId"];
wfIdText.Text = wfID;

一旦您進行第一次呼叫工作流程服務,內容要自動填入的工作流程執行個體識別碼的內容繫結,在服務端點上。

使用新建立的 Proxy 與先前已建立的工作流程執行個體通訊時, 您可以使用類似的方法來設定識別項中內容以確保您的郵件路由傳送到正確的工作流程] 執行個體,如下所示:

IContextManager mgr = ((IClientChannel)proxy).GetProperty<IContextManager>();
  mgr.SetContext(new Dictionary<string, string>{
    {"instanceId", wfIdText.Text}
  });

當您第一次有新建立的 Proxy [細緻這個程式碼可以運作,但不是如果您嘗試將內容設定第二次叫用另一個工作流程執行個體。 您收到的錯誤會告訴您啟用自動內容管理時無法變更內容。 基本上,您會告知您 can’t 有將蛋糕,且它太吃。 視內容,以便自動管理您 can’t 手動操作。 不幸的是,如果想手動管理內容您無法取得表示如我先前示範,您無法從內容擷取工作流程執行個體識別碼的自動管理。

若要處理這個不相符您處理每個案例分別。 工作流程的初始呼叫,使用新的 Proxy 但對所有的後續呼叫現有的工作流程執行個體,使用單一用戶端 Proxy 與手動管理內容。

為初始撥號您應該使用單一 ChannelFactory < TChannel > 建立的 Proxy。 這導致更佳的效能,因為 [ChannelFactory 建立有一些您不想重複每次第一個呼叫的負荷。 使用該更早版本中所示 的 圖 5 類似的程式碼可以使用單一 ChannelFactory < TChannel > 建立初始的 Proxy。 在您呼叫的程式碼中使用 Proxy 後您應該遵循呼叫 Close 方法釋出 Proxy 的最佳的作法。

這是建立您的 Proxy 使用通道 Factory 方法標準 WCF 程式碼。 因為繫結內容繫結您會取得自動內容管理,這表示您可以從內容擷取工作流程執行個體識別碼,進行至工作流程的第一次呼叫之後的預設。

為進行後續呼叫,您必須自行,管理內容,而且這需要使用不是經常由開發人員使用的 WCF 用戶端程式碼。 若要手動設定內容,您必須使用一個 OperationContextScope 自行建立 [MessageContextProperty。 MessageContextProperty 便設定郵件上,如同它正在傳送,也就是等於使用 [IContextManager 來設定內容與例外狀況直接使用屬性適用甚至當內容管理已停用。 圖 8 顯示程式碼來建立使用初始 Proxy 所使用之相同 ChannelFactory < TChannel > Proxy。 不同之處在於在這種情況下 [IContextManager 用來停用自動內容管理功能,並使用快取的 Proxy 而不是建立一個新在每個要求上的。

圖 8 停用自動內容管理

App a = (App)Application.Current;

if (updateProxy == null)
{
    if (factory == null)
        factory = new ChannelFactory<IWorkflowInterface>(
            a.LocalBinding, a.WFAddress);

        updateProxy = factory.CreateChannel();
        IContextManager mgr =
            ((IClientChannel)updateProxy).GetProperty<IContextManager>();
        mgr.Enabled = false;
        ((IClientChannel)updateProxy).Open();
}

一旦建立 Proxy 您需要建立一個 OperationContextScope,並將 [MessageContextProperty 新增到外寄訊息屬性範圍上。 這可讓屬性以包含在外寄的郵件在領域的這段期間。 圖 9 顯示程式碼,以建立並設定使用 [OperationContextScope 訊息屬性。

圖 9 使用 OperationContextScope

using (OperationContextScope scope =
    new OperationContextScope((IContextChannel)proxy))
{
    ContextMessageProperty property = new ContextMessageProperty(
        new Dictionary<string, string>
        {
            {“instanceId”, wfIdText.Text}
        });

OperationContext.Current.OutgoingMessageProperties.Add(
    "ContextMessageProperty", property);

proxy.UpdateOrder(
    new Order
        {
            CustomerName = "Matt",
            OrderID = 2,
            OrderTotal = 250.00,
            OrderStatus = "Updated"
        });
}

這可能似乎相當一點的只是要交談主應用程式和工作流程之間的工作。 好消息是大部分的這個邏輯和管理的識別項可以以幾個類別封裝。 不過,它不會牽涉到撰寫您的用戶端程式碼以確保內容正確地管理這些情況下,您需要將多個訊息傳送給工作流程執行個體以特定方式。 在本文的可下載程式碼,包含使用嘗試封裝大部分複雜性的本機通訊的工作流程的範例主機並範例應用程式示範如何使用主機]。

關於使用者介面互動 Word

將資料從工作流程傳送到主機的主要原因之一就是您想要呈現給使用者在應用程式介面。 幸運的是,使用這個模型時,您可使用利用使用者介面功能包括資料繫結在 WPF 中的某些選項。 簡單的範例如果您想您的使用者介面來使用資料繫結及更新使用者介面,從工作流程接收到的資料時您可以將繫結至使用者介面直接主機 ’s 服務執行個體。

使用您的視窗做為資料內容服務執行個體的關鍵是執行個體需要裝載成一個單一物件。 當您主控服務以單一時,您擁有存取執行個體,並且可以在您的使用者介面中使用它。 的 圖 10 所示的簡單的主機服務時它收到來自工作流程的資訊,並使用 [INotifyPropertyChangedInterface 來幫助資料繫結基礎結構立即挑選所做的變更,請更新屬性。 請注意 ServiceBehavior 屬性,指出這個類別應該裝載成一個單一物件。 如果您看回為 的 圖 7,您可以看到 ServiceHost 執行個體化不使用型別,但與類別的執行個體。

圖 10 的 INotifyPropertyChanged 與服務實作

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
internal class HostService : IHostInterface, INotifyPropertyChanged
{
    public void OrderStatusChange(Order order, string newStatus,
        string oldStatus)
    {
        CurrentMessage = String.Format("Order status changed to {0}",
            newStatus);
    }

private string msg;

public string CurrentMessage {
get { return msg; }
set
    {
        msg = value;
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(
                "CurrentMessage"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

要繫結至這個值,可以設定視窗] 或 [在視窗中的特定控制項的 DataContext 與執行個體。 執行個體可擷取 ServiceHost] 類別上使用 SingletonInstance 屬性,如下所示:

HostService host = ((App)Application.Current).appHost.SingletonInstance as HostService;
  if (host != null)
    this.DataContext = host;

現在您可以將繫結只是至項目在您的視窗中之物件上的屬性與這個 TextBlock 所示:

<TextBlock Text="{Binding CurrentMessage}" Grid.Row="3" />

如我說這是一個簡單的範例,您可以執行的工作。 在實際的應用程式您可能會不直接繫結至服務執行個體,但改繫結至哪個兩者您的視窗與服務實作必須存取某些物件。

尋找繼續進行到 WF4

WF4 介紹幾個功能,將會使本機通訊透過 WCF 更簡單。 主要功能是不依賴此通訊協定的郵件相互關聯。 也就是工作流程執行個體識別碼使用仍是一個選項,但新的選項將會啟用訊息至相關根據訊息的內容。 所以,如果您的郵件的每一個包含訂單編號]、 [客戶編號] 或 [其他片段資料,您可以定義這些訊息之間的相互關聯,並且不需要使用支援內容管理的繫結。

此外,WPF 和 WF 都建置在相同的核心.NET Framework 第 4 版的 XAML API 的事實可能開放整合技術以新的方式的一些有趣的可能性。 如同我們會得到更接近發行的.NET Framework 4,我會提供更多詳細資料與 WCF 和 WPF,整合 WF 連同 WF4 的內部運作上的其他內容。

Matt Milner 是技術人員在他專注在連接的系統技術 (WCF、 Windows 工作流程基礎、 BizTalk、 「 都柏林 」 及 Azure 服務平台) 的 Pluralsight 的成員。 Matt 也是在 Microsoft.NET 應用程式的設計和開發珍貴的獨立顧問。 他定期藉由在大型演講上發表專題例如 Tech·Ed 本機、 地區和國際演說共用他愛的技術。 Microsoft 已辨識 Milner 為他社群的貢獻周圍連接的系統技術的 MVP。 您可以連絡他透過在 pluralsight.com/community/blogs/matt 他部落格。