Zugreifen auf OperationContext aus einem Workflowdienst
Dieses Thema gilt für Windows Workflow Foundation 4.
Für den Zugriff auf den OperationContext in einem Workflowdienst müssen Sie die IReceiveMessageCallback-Schnittstelle in einer benutzerdefinierten Ausführungseigenschaft implementieren. Überschreiben Sie die OnReceiveMessage-Methode, die als Verweis an den OperationContext übergeben wird. In diesem Thema erhalten Sie schrittweise Anweisungen zum Implementieren dieser Ausführungseigenschaft, um einen benutzerdefinierten Header sowie eine benutzerdefinierte Aktivität abzurufen, die diese Eigenschaft zur Laufzeit für Receive sichtbar macht. Die benutzerdefinierte Aktivität implementiert dasselbe Verhalten wie eine Sequence-Aktivität, wenn jedoch Receive darin platziert wird, wird IReceiveMessageCallback aufgerufen, und die OperationContext-Informationen werden abgerufen. In diesem Thema wird zudem veranschaulicht, wie auf den clientseitigen OperationContext zugegriffen wird, um über die ISendMessageCallback-Schnittstelle ausgehende Header hinzuzufügen.
Implementieren des dienstseitigen IReceiveMessageCallback
Erstellen Sie eine leere Visual Studio 2010-Projektmappe.
Fügen Sie der Projektmappe eine neue Konsolenanwendung mit dem Namen
Service
hinzu.Fügen Sie Verweise auf die folgenden Assemblys hinzu:
System.Runtime.Serialization
System.ServiceModel
System.ServiceModel.Activities
Fügen Sie eine neue Klasse mit dem Namen
ReceiveInstanceIdCallback
hinzu, und implementieren Sie IReceiveMessageCallback, wie im folgenden Beispiel veranschaulicht.class ReceiveInstanceIdCallback : IReceiveMessageCallback { public const string HeaderName = "InstanceIdHeader"; public const string HeaderNS = "http://Microsoft.Samples.AccessingOperationContext"; public void OnReceiveMessage(System.ServiceModel.OperationContext operationContext, System.Activities.ExecutionProperties activityExecutionProperties) { try { Guid instanceId = operationContext.IncomingMessageHeaders.GetHeader<Guid>(HeaderName, HeaderNS); Console.WriteLine("Received a message from a workflow with instanceId = {0}", instanceId); } catch (MessageHeaderException) { Console.WriteLine("This message must not be from a workflow."); } } }
In diesem Code wird mit dem an die Methode übergebenen OperationContext auf die Header der eingehenden Nachricht zugegriffen.
Implementieren einer dienstseitigen systemeigenen Aktivität, um dem NativeActivityContext die IReceiveMessageCallback-Implementierung hinzuzufügen
Fügen Sie eine neue, von NativeActivity abgeleitete Klasse mit dem Namen
ReceiveInstanceIdScope
hinzu.Fügen Sie lokale Variablen hinzu, um untergeordnete Aktivitäten, Variablen, den aktuellen Aktivitätsindex und einen CompletionCallback-Rückruf zu verfolgen.
public sealed class ReceiveInstanceIdScope : NativeActivity { Collection<Activity> children; Collection<Variable> variables; Variable<int> currentIndex; CompletionCallback onChildComplete; }
Implementieren des Konstruktors
public ReceiveInstanceIdScope() : base() { this.children = new Collection<Activity>(); this.variables = new Collection<Variable>(); this.currentIndex = new Variable<int>(); } }
Implementieren Sie die
Activities
-Eigenschaft und dieVariables
-Eigenschaft.public Collection<Activity> Activities { get { return this.children; } } public Collection<Variable> Variables { get { return this.variables; } }
Überschreiben Sie CacheMetadata.
protected override void CacheMetadata(NativeActivityMetadata metadata) { //call base.CacheMetadata to add the Activities and Variables to this activity's metadata base.CacheMetadata(metadata); //add the private implementation variable: currentIndex metadata.AddImplementationVariable(this.currentIndex); }
Überschreiben Sie Execute.
protected override void Execute( NativeActivityContext context) { context.Properties.Add("ReceiveInstanceIdCallback", new ReceiveInstanceIdCallback()); InternalExecute(context, null); } void InternalExecute(NativeActivityContext context, ActivityInstance instance) { //grab the index of the current Activity int currentActivityIndex = this.currentIndex.Get(context); if (currentActivityIndex == Activities.Count) { //if the currentActivityIndex is equal to the count of MySequence's Activities //MySequence is complete return; } if (this.onChildComplete == null) { //on completion of the current child, have the runtime call back on this method this.onChildComplete = new CompletionCallback(InternalExecute); } //grab the next Activity in MySequence.Activities and schedule it Activity nextChild = Activities[currentActivityIndex]; context.ScheduleActivity(nextChild, this.onChildComplete); //increment the currentIndex this.currentIndex.Set(context, ++currentActivityIndex); }
Implementieren des Workflowdiensts
Öffnen Sie die vorhandene
Program
-Klasse.Definieren Sie die folgenden Konstanten:
class Program { const string addr = "https://localhost:8080/Service"; static XName contract = XName.Get("IService", "http://tempuri.org"); }
Fügen Sie eine statische Methode mit dem Namen
GetWorkflowService
hinzu, die den Workflowdienst erstellt.static Activity GetServiceWorkflow() { Variable<string> echoString = new Variable<string>(); Receive echoRequest = new Receive { CanCreateInstance = true, ServiceContractName = contract, OperationName = "Echo", Content = new ReceiveParametersContent() { Parameters = { { "echoString", new OutArgument<string>(echoString) } } } }; return new ReceiveInstanceIdScope { Variables = { echoString }, Activities = { echoRequest, new WriteLine { Text = new InArgument<string>( (e) => "Received: " + echoString.Get(e) ) }, new SendReply { Request = echoRequest, Content = new SendParametersContent() { Parameters = { { "result", new InArgument<string>(echoString) } } } } } }; }
Hosten Sie den Workflowdienst in der vorhandenen
Main
-Methode.static void Main(string[] args) { string addr = "https://localhost:8080/Service"; using (WorkflowServiceHost host = new WorkflowServiceHost(GetServiceWorkflow())) { host.AddServiceEndpoint(contract, new BasicHttpBinding(), addr); host.Open(); Console.WriteLine("Service waiting at: " + addr); Console.WriteLine("Press [ENTER] to exit"); Console.ReadLine(); host.Close(); } }
Implementieren des clientseitigen ISendMessageCallback
Fügen Sie der Projektmappe eine neue Konsolenanwendung mit dem Namen
Service
hinzu.Fügen Sie Verweise auf die folgenden Assemblys hinzu:
System.Runtime.Serialization
System.ServiceModel
System.ServiceModel.Activities
Fügen Sie eine neue Klasse mit dem Namen
SendInstanceIdCallback
hinzu, und implementieren Sie ISendMessageCallback, wie im folgenden Beispiel veranschaulicht.class SendInstanceIdCallback : ISendMessageCallback { public const string HeaderName = "InstanceIdHeader"; public const string HeaderNS = "http://Microsoft.Samples.AccessingOperationContext"; public Guid InstanceId { get; set; } public void OnSendMessage(System.ServiceModel.OperationContext operationContext) { operationContext.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader(HeaderName, HeaderNS, this.InstanceId)); } }
In diesem Code wird mit dem an die Methode übergebenen OperationContext der eingehenden Nachricht ein benutzerdefinierter Header hinzugefügt.
Implementieren einer clientseitigen systemeigenen Aktivität, um dem NativeActivityContext die clientseitige ISendMessageCallback-Implementierung hinzuzufügen
Fügen Sie eine neue, von NativeActivity abgeleitete Klasse mit dem Namen
SendInstanceIdScope
hinzu.Fügen Sie lokale Variablen hinzu, um untergeordnete Aktivitäten, Variablen, den aktuellen Aktivitätsindex und einen CompletionCallback-Rückruf zu verfolgen.
public sealed class SendInstanceIdScope : NativeActivity { Collection<Activity> children; Collection<Variable> variables; Variable<int> currentIndex; CompletionCallback onChildComplete; }
Implementieren des Konstruktors
public SendInstanceIdScope() : base() { this.children = new Collection<Activity>(); this.variables = new Collection<Variable>(); this.currentIndex = new Variable<int>(); }
Implementieren Sie die
Activities
-Eigenschaft und dieVariables
-Eigenschaft.public Collection<Activity> Activities { get { return this.children; } } public Collection<Variable> Variables { get { return this.variables; } }
Überschreiben Sie CacheMetadata.
protected override void CacheMetadata(NativeActivityMetadata metadata) { //call base.CacheMetadata to add the Activities and Variables to this activity's metadata base.CacheMetadata(metadata); //add the private implementation variable: currentIndex metadata.AddImplementationVariable(this.currentIndex); }
Überschreiben Sie Execute.
protected override void Execute( NativeActivityContext context) { context.Properties.Add("SendInstanceIdCallback", new SendInstanceIdCallback() { InstanceId = context.WorkflowInstanceId }); InternalExecute(context, null); } void InternalExecute(NativeActivityContext context, ActivityInstance instance) { //grab the index of the current Activity int currentActivityIndex = this.currentIndex.Get(context); if (currentActivityIndex == Activities.Count) { //if the currentActivityIndex is equal to the count of MySequence's Activities //MySequence is complete return; } if (this.onChildComplete == null) { //on completion of the current child, have the runtime call back on this method this.onChildComplete = new CompletionCallback(InternalExecute); } //grab the next Activity in MySequence.Activities and schedule it Activity nextChild = Activities[currentActivityIndex]; context.ScheduleActivity(nextChild, this.onChildComplete); //increment the currentIndex this.currentIndex.Set(context, ++currentActivityIndex); } protected override void Execute( NativeActivityContext context) { context.Properties.Add("ReceiveInstanceIdCallback", new ReceiveInstanceIdCallback()); InternalExecute(context, null); } void InternalExecute(NativeActivityContext context, ActivityInstance instance) { //grab the index of the current Activity int currentActivityIndex = this.currentIndex.Get(context); if (currentActivityIndex == Activities.Count) { //if the currentActivityIndex is equal to the count of MySequence's Activities //MySequence is complete return; } if (this.onChildComplete == null) { //on completion of the current child, have the runtime call back on this method this.onChildComplete = new CompletionCallback(InternalExecute); } //grab the next Activity in MySequence.Activities and schedule it Activity nextChild = Activities[currentActivityIndex]; context.ScheduleActivity(nextChild, this.onChildComplete); //increment the currentIndex this.currentIndex.Set(context, ++currentActivityIndex); }
Implementieren eines Workflowclients
Erstellen Sie ein neues Konsolenanwendungsprojekt mit dem Namen
Client
.Fügen Sie Verweise auf die folgenden Assemblys hinzu:
System.Activities
System.ServiceModel
System.ServiceModel.Activities
Öffnen Sie die generierte Datei Program.cs, und fügen Sie eine statische Methode mit dem Namen
GetClientWorkflow
hinzu, um den Clientworkflow zu erstellen.static Activity GetClientWorkflow() { Variable<string> echoString = new Variable<string>(); // Define the endpoint Endpoint clientEndpoint = new Endpoint { Binding = new BasicHttpBinding(), AddressUri = new Uri("https://localhost:8080/Service") }; // Configure the Send activity used to send a message Send echoRequest = new Send { Endpoint = clientEndpoint, ServiceContractName = XName.Get("IService", "http://tempuri.org"), OperationName = "Echo", Content = new SendParametersContent() { Parameters = { { "echoString", new InArgument<string>("Hello, World") } } } }; // Place the Send activity in a SendInstanceIdScope. This hooks up the ISendMessageCallback // implementation to the client workflow. return new SendInstanceIdScope { Variables = { echoString }, Activities = { new CorrelationScope { Body = new Sequence { Activities = { // Send the request message echoRequest, // Receive the reply from the service new ReceiveReply { Request = echoRequest, Content = new ReceiveParametersContent { Parameters = { { "result", new OutArgument<string>(echoString) } } } } } } }, new WriteLine { Text = new InArgument<string>( (e) => "Received Text: " + echoString.Get(e) ) }, } }; }
Fügen Sie der
Main()
-Methode den folgenden Hostcode hinzu.static void Main(string[] args) { Activity workflow = GetClientWorkflow(); WorkflowInvoker.Invoke(workflow); WorkflowInvoker.Invoke(workflow); Console.WriteLine("Press [ENTER] to exit"); Console.ReadLine(); }
Beispiel
Im Folgenden finden Sie eine vollständige Auflistung des in diesem Thema verwendeten Quellcodes.
// ReceiveInstanceIdScope.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
using System.Activities;
using System.Collections.ObjectModel;
namespace Microsoft.Samples.AccessingOperationContext.Service
{
public sealed class ReceiveInstanceIdScope : NativeActivity
{
Collection<Activity> children;
Collection<Variable> variables;
Variable<int> currentIndex;
CompletionCallback onChildComplete;
public ReceiveInstanceIdScope()
: base()
{
this.children = new Collection<Activity>();
this.variables = new Collection<Variable>();
this.currentIndex = new Variable<int>();
}
public Collection<Activity> Activities
{
get
{
return this.children;
}
}
public Collection<Variable> Variables
{
get
{
return this.variables;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
//call base.CacheMetadata to add the Activities and Variables to this activity's metadata
base.CacheMetadata(metadata);
//add the private implementation variable: currentIndex
metadata.AddImplementationVariable(this.currentIndex);
}
protected override void Execute(
NativeActivityContext context)
{
context.Properties.Add("ReceiveInstanceIdCallback", new ReceiveInstanceIdCallback());
InternalExecute(context, null);
}
void InternalExecute(NativeActivityContext context, ActivityInstance instance)
{
//grab the index of the current Activity
int currentActivityIndex = this.currentIndex.Get(context);
if (currentActivityIndex == Activities.Count)
{
//if the currentActivityIndex is equal to the count of MySequence's Activities
//MySequence is complete
return;
}
if (this.onChildComplete == null)
{
//on completion of the current child, have the runtime call back on this method
this.onChildComplete = new CompletionCallback(InternalExecute);
}
//grab the next Activity in MySequence.Activities and schedule it
Activity nextChild = Activities[currentActivityIndex];
context.ScheduleActivity(nextChild, this.onChildComplete);
//increment the currentIndex
this.currentIndex.Set(context, ++currentActivityIndex);
}
}
}
// ReceiveInstanceIdScope.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
using System;
using System.ServiceModel;
using System.ServiceModel.Activities;
namespace Microsoft.Samples.AccessingOperationContext.Service
{
class ReceiveInstanceIdCallback : IReceiveMessageCallback
{
public const string HeaderName = "InstanceIdHeader";
public const string HeaderNS = "http://Microsoft.Samples.AccessingOperationContext";
public void OnReceiveMessage(System.ServiceModel.OperationContext operationContext, System.Activities.ExecutionProperties activityExecutionProperties)
{
try
{
Guid instanceId = operationContext.IncomingMessageHeaders.GetHeader<Guid>(HeaderName, HeaderNS);
Console.WriteLine("Received a message from a workflow with instanceId = {0}", instanceId);
}
catch (MessageHeaderException)
{
Console.WriteLine("This message must not be from a workflow.");
}
}
}
}
// Service.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
using System;
using System.Activities;
using System.Activities.Statements;
using System.ServiceModel;
using System.ServiceModel.Activities;
using System.Xml.Linq;
namespace Microsoft.Samples.AccessingOperationContext.Service
{
class Program
{
const string addr = "https://localhost:8080/Service";
static XName contract = XName.Get("IService", "http://tempuri.org");
static void Main(string[] args)
{
string addr = "https://localhost:8080/Service";
using (WorkflowServiceHost host = new WorkflowServiceHost(GetServiceWorkflow()))
{
host.AddServiceEndpoint(contract, new BasicHttpBinding(), addr);
host.Open();
Console.WriteLine("Service waiting at: " + addr);
Console.WriteLine("Press [ENTER] to exit");
Console.ReadLine();
host.Close();
}
}
static Activity GetServiceWorkflow()
{
Variable<string> echoString = new Variable<string>();
Receive echoRequest = new Receive
{
CanCreateInstance = true,
ServiceContractName = contract,
OperationName = "Echo",
Content = new ReceiveParametersContent()
{
Parameters = { { "echoString", new OutArgument<string>(echoString) } }
}
};
return new ReceiveInstanceIdScope
{
Variables = { echoString },
Activities =
{
echoRequest,
new WriteLine { Text = new InArgument<string>( (e) => "Received: " + echoString.Get(e) ) },
new SendReply
{
Request = echoRequest,
Content = new SendParametersContent()
{
Parameters = { { "result", new InArgument<string>(echoString) } }
}
}
}
};
}
}
}
// SendInstanceIdCallback.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
using System;
using System.ServiceModel.Activities;
using System.ServiceModel.Channels;
namespace Microsoft.Samples.AccessingOperationContext.Client
{
class SendInstanceIdCallback : ISendMessageCallback
{
public const string HeaderName = "InstanceIdHeader";
public const string HeaderNS = "http://Microsoft.Samples.AccessingOperationContext";
public Guid InstanceId { get; set; }
public void OnSendMessage(System.ServiceModel.OperationContext operationContext)
{
operationContext.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader(HeaderName, HeaderNS, this.InstanceId));
}
}
}
// SendInstanceIdScope.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
using System.Activities;
using System.Collections.ObjectModel;
namespace Microsoft.Samples.AccessingOperationContext.Client
{
public sealed class SendInstanceIdScope : NativeActivity
{
Collection<Activity> children;
Collection<Variable> variables;
Variable<int> currentIndex;
CompletionCallback onChildComplete;
public SendInstanceIdScope()
: base()
{
this.children = new Collection<Activity>();
this.variables = new Collection<Variable>();
this.currentIndex = new Variable<int>();
}
public Collection<Activity> Activities
{
get
{
return this.children;
}
}
public Collection<Variable> Variables
{
get
{
return this.variables;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
//call base.CacheMetadata to add the Activities and Variables to this activity's metadata
base.CacheMetadata(metadata);
//add the private implementation variable: currentIndex
metadata.AddImplementationVariable(this.currentIndex);
}
protected override void Execute(
NativeActivityContext context)
{
context.Properties.Add("SendInstanceIdCallback", new SendInstanceIdCallback() { InstanceId = context.WorkflowInstanceId });
InternalExecute(context, null);
}
void InternalExecute(NativeActivityContext context, ActivityInstance instance)
{
//grab the index of the current Activity
int currentActivityIndex = this.currentIndex.Get(context);
if (currentActivityIndex == Activities.Count)
{
//if the currentActivityIndex is equal to the count of MySequence's Activities
//MySequence is complete
return;
}
if (this.onChildComplete == null)
{
//on completion of the current child, have the runtime call back on this method
this.onChildComplete = new CompletionCallback(InternalExecute);
}
//grab the next Activity in MySequence.Activities and schedule it
Activity nextChild = Activities[currentActivityIndex];
context.ScheduleActivity(nextChild, this.onChildComplete);
//increment the currentIndex
this.currentIndex.Set(context, ++currentActivityIndex);
}
}
}
// Client.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
using System;
using System.Activities;
using System.Activities.Statements;
using System.ServiceModel;
using System.ServiceModel.Activities;
using System.Xml.Linq;
namespace Microsoft.Samples.AccessingOperationContext.Client
{
class Program
{
static void Main(string[] args)
{
Activity workflow = GetClientWorkflow();
WorkflowInvoker.Invoke(workflow);
WorkflowInvoker.Invoke(workflow);
Console.WriteLine("Press [ENTER] to exit");
Console.ReadLine();
}
static Activity GetClientWorkflow()
{
Variable<string> echoString = new Variable<string>();
Endpoint clientEndpoint = new Endpoint
{
Binding = new BasicHttpBinding(),
AddressUri = new Uri("https://localhost:8080/Service")
};
Send echoRequest = new Send
{
Endpoint = clientEndpoint,
ServiceContractName = XName.Get("IService", "http://tempuri.org"),
OperationName = "Echo",
Content = new SendParametersContent()
{
Parameters = { { "echoString", new InArgument<string>("Hello, World") } }
}
};
return new SendInstanceIdScope
{
Variables = { echoString },
Activities =
{
new CorrelationScope
{
Body = new Sequence
{
Activities =
{
echoRequest,
new ReceiveReply
{
Request = echoRequest,
Content = new ReceiveParametersContent
{
Parameters = { { "result", new OutArgument<string>(echoString) } }
}
}
}
}
},
new WriteLine { Text = new InArgument<string>( (e) => "Received Text: " + echoString.Get(e) ) },
}
};
}
}
}
Optionale Kommentare.
Siehe auch
Aufgaben
Zugreifen auf OperationContext
Konzepte
Erstellen von Workflows mit imperativem Code