client.Open();//打开WCF客户端
client.GetAllCcCommunicateCompleted += new EventHandler<GoSun.WcfFache.CCMan.GetAllCcCommunicateCompletedEventArgs>(client_GetAllCcCommunicateCompleted);//注册事件句柄,用于异步执行完毕后通知界面client.GetAllCcCommunicateAsync();//异步调用
client.GetAllCcCommunicateCompleted += new EventHandler<GoSun.WcfFache.CCMan.GetAllCcCommunicateCompletedEventArgs>(client_GetAllCcCommunicateCompleted);//注册事件句柄,用于异步执行完毕后通知界面client.GetAllCcCommunicateAsync();//异步调用
http://www.codeproject.com/KB/XML/MTOMWebServices.aspx
Search Articles Authors Advanced Search
Sitemap
Print Broken Article? Book Discuss Send to a friend
4 votes for this article.
Popularity: 2.86. Rating: 4.75 out of 5.
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report this article.Download source
Contents
Features
Introduction
Concept and Design Implementation
Test
Appendix
Conclusion
Features
Microsoft Windows Communication Foundation (WCF) and Windows Workflow Foundation (WF) Technologies
No custom activity to receive an event message
Based on mapping the interface contracts between the WCF and Workflow technologies
Transaction support
Declaratively programming
No require to build a Local Service
Null Workflow Local Service for correlated events
Null WCF Service
Capability of the pre/post processing in the operation invoker
Working side by side with a Workflow Local Service (ExternalDataExchangeService)
Introduction
The Microsoft WF Technology enables to receive external (host) events via a tightly coupled Local Service layer. This layer simplifies the connectivity between the workflow and host application based on the delegate/event pattern. All this magic plumbing is hidden by ExternalDataExchangeService class that intercepts a Local Service and generates an event message to the specific Workflow queue. In order to receive an event message by event driven Activity, the ExternalDataExchange interface contract must be declared with an application specific event handler. The event delegate has a standard .Net signature shown in the following line: public void EventHandler(object sender, ExternalDataEventArgs eventArgs);
where the eventArgs represents a Workflow event interest such as InstanceId, Identity, application specific data, etc.The interface contract metadata is used to create a unique workflow queue based on the interface type, event name and collection of the properties for correlation pattern as an additional option. Note, the workflow queue messaging is the only way how the host application can talk to the workflow. From the connectivity point of the view, the Workflow represents an event sink and the WCF client's event source (subscriber interest). The following picture shows this logical pattern:On the event source side, we can create many kinds of patterns (such as Remoting, WSE, WCF, etc.) to generate an event message and send it to the local or remote workflow queue. This article describes how this connectivity cab be achieved using the WCF paradigm based on the interface contracts defined differently at each end sides. You will see how event source can be logically (declaratively) connected with the event sink metadata together.The following picture shows a representation of the loosely coupled connectivity between the WCF event source and WF event sink: Note, the Workflow side doesn't need to have the Local Service layer in order to generate an event message. For this WCF/WF plumbing we need only metadata of the interface contracts and of course a generic decorator of the operation in order to make all magic work. The service operation contract can be mapped to the specific WF interface contract explicitly using the WorkflowFireEventAttribute. Behind this attribute is a plumbing code to generate an event message and that is sent to the workflow queue. Using the WCF connectivity to deliver an event message to the Workflow event sink (such as HandleExternalEventActivity) enables building an application model with a capability of the WCF paradigm based on the Address, Binding and Contract. For example: the netMsmgBinding binding for delivery event message in the async and disconnected manner or the webHttpBinding binding to enable a connectivity for delivery an event message from the browser driven by Ajax/Json (Orcas CTP version). Let's look at in detail, how the WCF/WF plumbing is done for dispatching an even message to the workflow queue. I will assume that you are familiar with these technologies. In addition, I mentioned earlier, the Workflow side doesn't use any custom activity for this plumbing, so it is a standard workflow definition (sequential or state machine) using a HandleExternalEventActivity, therefore I will focus on the plumbing concept and implementation.Concept and Design Implementation
The concept of the loosely coupled WCF/WF connectivity for sending a workflow event message is based on the interceptor (WorkflowFireEventAttribute) on the service operation contract, which will map it to the specific ExternalDataExchange interface contract (see the above picture).The WCF service is hosted in the same appDomain with a WorkflowRuntime. Basically we need to create a Null Service derived from our WCF Interface contract. The following is an example of the simple Null service and Interface contracts:// WCF
[ServiceContract]
public interface IFireEventTest
{
[OperationContract(IsOneWay = true)]
void Approve(object sender, ExternalDataEventArgs e);
}
public class ServiceTest1 : IFireEventTest
{
[WorkflowFireEvent(EventType = typeof(IWorkflowSimpleEvents))]
public void Approve(object sender, ExternalDataEventArgs e) { }
}
// Workflow EventType
[ExternalDataExchange]
public interface IWorkflowSimpleEvents
{
event EventHandler<ExternalDataEventArgs> Approve;
}
During the start up appDomain, the WorkflowFireEventAttribute will insert a WorkflowFireEventOperationInvoker to the OperationInvoker to intercept a call before invoking a service method. The following code snippet shows a method, where it is going to dispatch a workflow event message: Collapse
object IOperationInvoker.Invoke(object instance, object[] inputs, out object[] outputs)
{
object retVal = null;
outputs = new object[0];
// validation for mandatory inputs
if (inputs.Length < 2 || !typeof(ExternalDataEventArgs).IsInstanceOfType(inputs[1]))
throw new InvalidOperationException(string.Format(" ... ");
// mandatory inputs
object sender = inputs[0];
ExternalDataEventArgs eventArgs = inputs[1] as ExternalDataEventArgs;
if (Transaction.Current != null &&
Transaction.Current.TransactionInformation.Status == TransactionStatus.Active)
{
WorkflowInstance workflowInstance = null;
eventArgs.WaitForIdle = false;
#region action - transactional delivery to the workflow queue
using(TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))
{
// pre-processing
if (this.Option == WorkflowFireEventOption.After)
{
retVal = this._innerOperationInvoker.Invoke(instance, inputs, out outputs);
}
// fire event
workflowInstance =
WorkflowHelper.DispatchEvent(this.EventType,this.EventName,sender,eventArgs,true);
// storing a workflow into the database
workflowInstance.Unload();
// post-process
if (this.Option == WorkflowFireEventOption.Before)
{
retVal = this._innerOperationInvoker.Invoke(instance, inputs, out outputs);
}
// done
ts.Complete();
}
try
{
// workaround to detect WorkflowStatus
workflowInstance.Start();
}
catch (Exception ex)
{
// WorkflowStatus != Created (therefore we can go ahead and use it)
Trace.WriteLine(ex.Message);
}
#endregion
}
else
{
#region action - non-transactional delivery to the workflow queue
// pre-processing
if (this.Option == WorkflowFireEventOption.After)
{
retVal = this._innerOperationInvoker.Invoke(instance, inputs, out outputs);
}
// dispatching event
WorkflowHelper.DispatchEvent(this.EventType, this.EventName, sender, eventArgs);
// post-processing
if (this.Option == WorkflowFireEventOption.Before)
{
retVal = this._innerOperationInvoker.Invoke(instance, inputs, out outputs);
}
#endregion
}
return retVal;
}