Accesso alle informazioni sull'identità in un servizio del flusso di lavoro
Le informazioni contenute in questo argomento sono valide per Windows Workflow Foundation 4.
Per accedere alle informazioni di identità in un servizio del flusso di lavoro, è necessario implementare l'interfaccia IReceiveMessageCallback in una proprietà di esecuzione personalizzata. Nel metodo OnReceiveMessage è possibile accedere all'elemento ServiceSecurityContext per accedere alle informazioni di identità. In questo argomento viene descritta l'implementazione della proprietà di esecuzione e di un'attività personalizzata che esporrà tale proprietà all'attività Receive in fase di esecuzione. L'attività personalizzata implementerà lo stesso comportamento di un'attività Sequence, ad eccezione del fatto che quando un oggetto Receive viene posizionato all'interno dell'attività, verrà chiamato IReceiveMessageCallback e le informazioni di identità verranno recuperate.
Implementare IReceiveMessageCallback
Creare una soluzione Visual Studio vuota.
Aggiungere alla soluzione una nuova applicazione console denominata
Service
.Aggiungere riferimenti agli assembly riportati di seguito:
System.Runtime.Serialization
System.ServiceModel
System.ServiceModel.Activities
Aggiungere una nuova classe denominata
AccessIdentityCallback
e implementare IReceiveMessageCallback come illustrato nell'esempio seguente.class AccessIdentityCallback : IReceiveMessageCallback { public void OnReceiveMessage(System.ServiceModel.OperationContext operationContext, System.Activities.ExecutionProperties activityExecutionProperties) { try { Console.WriteLine("Received a message from a workflow with the following identity"); Console.WriteLine("Windows Identity Name: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.Name); Console.WriteLine("Windows Identity User: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.User); Console.WriteLine("Windows Identity IsAuthenticated: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.IsAuthenticated); } catch (Exception ex) { Console.WriteLine("An exception occurred: " + ex.Message); } } }
In questo codice viene utilizzato OperationContext passato al metodo per accedere alle informazioni di identità.
Implementare l'attività Native per aggiungere l'implementazione IReceiveMessageCallback a NativeActivityContext
Aggiungere una nuova classe derivata da NativeActivity denominata
AccessIdentityScope
.Aggiungere variabili locali per tenere traccia di attività figlio, variabili e indice dell'attività corrente e un callback CompletionCallback.
public sealed class AccessIdentityScope : NativeActivity { Collection<Activity> children; Collection<Variable> variables; Variable<int> currentIndex; CompletionCallback onChildComplete; }
Implementare il costruttore
public AccessIdentityScope() : base() { this.children = new Collection<Activity>(); this.variables = new Collection<Variable>(); this.currentIndex = new Variable<int>(); }
Implementare le proprietà
Activities
eVariables
.public Collection<Activity> Activities { get { return this.children; } } public Collection<Variable> Variables { get { return this.variables; } }
Eseguire l'override di 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); }
Eseguire l'override di Execute.
protected override void Execute(NativeActivityContext context) { // Add the IReceiveMessageCallback implementation as an Execution property context.Properties.Add("AccessIdentityCallback", new AccessIdentityCallback()); 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); }
Implementare il servizio di flusso di lavoro
Aprire la classe
Program
esistente.Definire le costanti seguenti:
class Program { const string addr = "https://localhost:8080/Service"; static XName contract = XName.Get("IService", "http://tempuri.org"); }
Aggiungere un metodo statico denominato
GetWorkflowService
che crea il servizio di flusso di lavoro.static Activity GetServiceWorkflow() { Variable<string> echoString = new Variable<string>(); // Create the Receive activity Receive echoRequest = new Receive { CanCreateInstance = true, ServiceContractName = contract, OperationName = "Echo", Content = new ReceiveParametersContent() { Parameters = { { "echoString", new OutArgument<string>(echoString) } } } }; return new AccessIdentityScope { 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) } } } } } }; }
Nel metodo
Main
esistente ospitare il servizio di flusso di lavoro.static void Main(string[] args) { string addr = "https://localhost:8080/Service"; using (WorkflowServiceHost host = new WorkflowServiceHost(GetServiceWorkflow())) { WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message); host.AddServiceEndpoint(contract, binding, addr); host.Open(); Console.WriteLine("Service waiting at: " + addr); Console.WriteLine("Press [ENTER] to exit"); Console.ReadLine(); host.Close(); } }
Implementare un client flusso di lavoro
Creare un nuovo progetto di applicazione console denominato
Client
.Aggiungere riferimenti agli assembly riportati di seguito:
System.Activities
System.ServiceModel
System.ServiceModel.Activities
Aprire il file Program.cs generato e aggiungere un metodo statico denominato
GetClientWorkflow
per creare il client flusso di lavoro.static Activity GetClientWorkflow() { Variable<string> echoString = new Variable<string>(); Endpoint clientEndpoint = new Endpoint { Binding = new WSHttpBinding(SecurityMode.Message), 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 Sequence { 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) ) }, } }; } }
Aggiungere il codice di hosting seguente per implementare il metodo
Main()
.static void Main(string[] args) { Activity workflow = GetClientWorkflow(); WorkflowInvoker.Invoke(workflow); WorkflowInvoker.Invoke(workflow); Console.WriteLine("Press [ENTER] to exit"); Console.ReadLine(); }
Esempio
Di seguito è riportato il codice sorgente utilizzato in questo argomento.
// AccessIdentityCallback.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
using System;
using System.ServiceModel;
using System.ServiceModel.Activities;
namespace Microsoft.Samples.AccessingOperationContext.Service
{
class AccessIdentityCallback : 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 the following identity" ); // with instanceId = {0}", instanceId);
Console.WriteLine("Windows Identity Name: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.Name);
Console.WriteLine("Windows Identity User: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.User);
Console.WriteLine("Windows Identity IsAuthenticated: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.IsAuthenticated);
}
catch (MessageHeaderException)
{
Console.WriteLine("This message must not be from a workflow.");
}
catch (Exception ex)
{
Console.WriteLine("An exception occurred: " + ex.Message);
}
}
}
}
// AccessIdentityScope.cs
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------
using System.Activities;
using System.Collections.ObjectModel;
namespace Microsoft.Samples.AccessingOperationContext.Service
{
public sealed class AccessIdentityScope : NativeActivity
{
Collection<Activity> children;
Collection<Variable> variables;
Variable<int> currentIndex;
CompletionCallback onChildComplete;
public AccessIdentityScope()
: 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("AccessIdentityCallback", new AccessIdentityCallback());
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);
}
}
}
// 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()))
{
WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message);
host.AddServiceEndpoint(contract, binding, 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 AccessIdentityScope
{
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) } }
}
}
}
};
}
}
}
// 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 WSHttpBinding(SecurityMode.Message),
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 Sequence
{
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) ) },
}
};
}
}
}
Vedere anche
Attività
Concetti
Creazione di flussi di lavoro tramite codice imperativo