Most of you know that .NET 4.0 with VS 2010 in Beta is already released. With this new framework version, lots of new features have been introduced. VS 2010 IDE has provided some excellent features for developers. With .NET 4.0, the new versions of technologies provided are WCF (Windows Communication Foundation) 4.0 and WF (Windows Workflow Foundation) 4.0, which has lots of improvements. Typically now WCF and WF are more integrated with each other. Workflow 4.0 has new types of activities as listed below:
-Procedural Activities.
-Flowchart.
-Messaging.
-PowerShell.
-Migration.
Amongst other improvements, Workflow 4.0 has performance improvements for Tracking, Persistence etc. In this article we will see how a WCF service can be created using WCF Messaging activities. This article is based on the Beta version and could possibly change in future.
Step 1: Open VS 2010 and create a blank solution. Click File > New > Other Project Types > Visual Studio Solutions > Blank Solution. Make sure .NET Framework 4.0 is selected. Name this solution as ‘WCF_WF_ServiceHostClient’.
Step 2: In this Solution add a new Project of type ‘Sequential Workflow Console Application’. Name this as ‘WCF_ServiceHost’.
Step 3: Once you add a new workflow project, you will get the following designer shown below. Rename ‘Sequence1.xaml’ to ‘WF_DataService’ . If you face compiler errors, then remove ‘Sequence1.xaml’ instead of renaming and add a new ‘Sequence’ activity and rename it as ‘WF_DataService’.
Step 4: Now select the Messaging Activity group from the tool bar and select ‘Receive and SendReply’ activity. This activity is used to accept requests from client and process the message. Once the message is processed, the reply for that message is sent back to the client. Drag and Drop the activity on to the Sequence activity. You will get the following view:
If you see the above workflow carefully, then you will find that ‘RequestAndSendReply’ activity itself is a sequence activity. It has two activities inside it, ‘Receive’ and ‘SendReply’. The ‘receive’ activity has by default an operation defined, which makes it clear that now it is not required for developers to declare an interface.
Step 5: In this application we are going to use more activities and will make our workflow a bit complex to demonstrate various capabilities of the framework. So to do this, add a new class in the Workflow project and name it as ‘InputOutput’. This class will have two classes which will be used for input and output parameters to and from workflow and a class which will have a method containing business logic. In the workflow project add the reference of ‘System.Runtime.Serialization’. The code for the class is as below:
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Runtime.Serialization;
namespace WCF_ServiceHost
{
[DataContract]
public class clsInput
{
[DataMember]
public string DbName { get; set; }
[DataMember]
public string TbName { get; set; }
}
[DataContract]
public class clsOutput
{
[DataMember]
public DataSet Ds { get; set; }
}
public class CDataAccess
{
public clsOutput RetDs(clsInput objIn)
{
string DbName = objIn.DbName;
string TbName = objIn.TbName;
SqlConnection Conn = new SqlConnection("Data Source=.;Initial Catalog=" + DbName + ";Integrated Security=SSPI");
SqlDataAdapter AdTable = new SqlDataAdapter("Select * from " + TbName, Conn);
DataSet Ds = new DataSet();
AdTable.Fill(Ds, TbName);
clsOutput objOp = new clsOutput();
objOp.Ds = Ds;
return objOp;
}
}
}
VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Data
Imports System.Data.SqlClient
Imports System.Runtime.Serialization
Namespace WCF_ServiceHost
<DataContract> _
Public Class clsInput
Private privateDbName As String
<DataMember> _
Public Property DbName() As String
Get
Return privateDbName
End Get
Set(ByVal value As String)
privateDbName = value
End Set
End Property
Private privateTbName As String
<DataMember> _
Public Property TbName() As String
Get
Return privateTbName
End Get
Set(ByVal value As String)
privateTbName = value
End Set
End Property
End Class
<DataContract> _
Public Class clsOutput
Private privateDs As DataSet
<DataMember> _
Public Property Ds() As DataSet
Get
Return privateDs
End Get
Set(ByVal value As DataSet)
privateDs = value
End Set
End Property
End Class
Public Class CDataAccess
Public Function RetDs(ByVal objIn As clsInput) As clsOutput
Dim DbName As String = objIn.DbName
Dim TbName As String = objIn.TbName
Dim Conn As New SqlConnection("Data Source=.;Initial Catalog=" & DbName & ";Integrated Security=SSPI")
Dim AdTable As New SqlDataAdapter("Select * from " & TbName, Conn)
Dim Ds As New DataSet()
AdTable.Fill(Ds, TbName)
Dim objOp As New clsOutput()
objOp.Ds = Ds
Return objOp
End Function
End Class
End Namespace
If you see the above code carefully, there are two classes named ‘clsInput’ and ‘clsOutput’ used for input and output parameters respectively. Properties declared in these two classes are decorated as ‘DataMember’ for serialization purpose. The class ‘CDataAccess’ contains method ‘RetDs’ which takes an object of ‘clsInput’ and input parameters and return an object of ‘clsOutput’.
Step 6: Now we will configure our workflow as below:
- Rename ‘DisplayName’ property of the Parent workflow to ‘WFDataService’
- Rename ‘DisplayName’ property of the ReceiveAndSendReply activity to ‘WFData Service Receive Reply’.
- Rename ‘DisplayName’ property of the ‘Receive’ activity to ‘Accept Request’
- Change ‘OperationName’ value of ‘Receive’ activity from ‘Operation1’ to ‘GetData’.
- Change ‘ServiceContractName’ value of ‘receive’ activity from ‘{http://tempuri.org/}IService’ to ‘MyDataServiceContract’.
Check the ‘CanCreateInstance’ check box. This will create an instance of the service object.
Step 7: Now we need to define variables which are going to act as input and output parameters. To do this, select ‘WFData Service Receive Reply’ activity and click on ‘Variable’ tab on the lower left corner. You can define variables here.
Create following variables:
Variable Name
|
Type
|
objInput
|
clsInput
|
objOutput
|
clsOutput
|
objDal
|
CDataAccess
|
To do this, type variable name in ‘Name’ column and then from the ‘Variable Type’ column select browse type, you will get the ‘Browse and select .NET Type’. Using this select type of which variable you want to define.
All variables declared should be displayed as below:
Step 8: Select ‘Accept Request’ activity and set its Value property to ‘objInput’, select ‘SendReply’ activity and set its Value property to ‘objPutput’ as below.
This actually completes the WF activity configuration. But now here the point is how can we make use of the ‘RetDs’ method from ‘CDataAccess’ class. Since this method is externally available, we need to make a call from the workflow to this class. If you are aware of ‘CallExternalMethod’ activity of WF 3.x, then it was possible for our WF to make call to methods in external classes. But now in WF 4.0, a new procedural activity has been provided to us named ‘InvokeMethod’ and ‘InvokeMethod<T>‘ activities. The difference between these two activities is that the ‘InvokeMethod<T>‘ has to be types to the return type from the method against this activity is configured.
Step 9: Drag and Drop the ‘InvokeMethod<T>‘ method. When you drag and drop, a dialog window will appear where you need to specify the type of the activity. This needs to be configured for the returned type from the method as below:
The WF will be displayed as below:
Red bubbled here shows that now you need to configure the WF.
The InvokeMethod<T> activity has following properties:
Property Name
|
Description
|
Target Type
|
To be set for the class from which method needs to be called.
|
Target Object
|
To be set against the object of the class from which method needs to be called.
|
Method Name
|
Name of the method to be called.
|
Parameters
|
Input parameters and its type, for the method.
|
Result
|
Result returned by method.
|
Step 10: Select the ‘InvokeMethod’ activity and select ‘Target Type’, you will get drop down. From this select ‘Browse for Types’ and select the type. In our case we will select ‘WCF_ServiceHost.CDataAccess’. Set ‘Method Name’ property to ‘RetDs’, the name of the method to be called. The important point here is how we can set input parameters for the method. To do this, select the ‘InvokeMethod’ activity and go to the property palate, click on the parameters (Note: This property takes input parameters collection) and you will get the following window:
This window allows us to set the ‘Direction’ of the parameter, the type of the parameter and most important the ‘Expression’, means the actual parameter name supposed to be passed to method. The important part here is that for the expression you must have variable for the ‘Argument Type’ declared for the workflow.
Since the return type of ‘RetDs’ method is ‘clsOutput’, so from the property palate, set the result property to ‘objOutput’.
Now the workflow will look as below:
Step 11: From the project open Program.cs file, this file currently contains workflow hosting logic. Since we are using WF as WCF service, we need to write logic for the workflow hosting. Remove the existing code from the ‘Main’ method and write the following code:
C#
namespace WCF_ServiceHost
{
using System;
using System.Linq;
using System.Threading;
using System.Activities;
using System.Activities.Statements;
using System.ServiceModel.Activities;
using System.ServiceModel.Description;
class Program
{
static void Main(string[] args)
{
string MyAddress = "http://localhost:1020/MyDataServiceApp";
using (WorkflowServiceHost Host = new WorkflowServiceHost(typeof(WCF_ServiceHost.WF_DataService), new Uri(MyAddress)))
{
Host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
Host.AddDefaultEndpoints();
Host.Open();
Console.WriteLine("Service is Listening at :" + MyAddress);
Console.WriteLine("Press ENTER to Exit");
Console.ReadLine();
Host.Close();
Console.ReadLine();
}
}
}
}
VB.NET
Imports System
Imports System.Linq
Imports System.Threading
Imports System.Activities
Imports System.Activities.Statements
Imports System.ServiceModel.Activities
Imports System.ServiceModel.Description
Namespace WCF_ServiceHost
Friend Class Program
Shared Sub Main(ByVal args() As String)
Dim MyAddress As String = "http://localhost:1020/MyDataServiceApp"
Using Host As New WorkflowServiceHost(GetType(WCF_ServiceHost.WF_DataService), New Uri(MyAddress))
Host.Description.Behaviors.Add(New ServiceMetadataBehavior() With {.HttpGetEnabled = True})
Host.AddDefaultEndpoints()
Host.Open()
Console.WriteLine("Service is Listening at :" & MyAddress)
Console.WriteLine("Press ENTER to Exit")
Console.ReadLine()
Host.Close()
Console.ReadLine()
End Using
End Sub
End Class
End Namespace
The above code shows that, ‘WorkflowServiceHost’ class is used for hosting workflow. Uri address specifies the service repository and also the communication address. The behavior for publishing metadata from the service is added.
Step 12: Run the project you will get the following output:
Step 13: To verify whether the service is started or not, keep the service running and copy the address specified in ‘Program.cs’. Start an instance of browser and paste the address, you should get the following output. If you get this output, then it means that service is ready:
Creating Client for this WCF service:
In this part we will create a windows client and consume the WCF service in it.
Step 1: In the same solution add a new Windows Forms Application, name it as ‘WCF_WindowsClient’. Design the WinForm as below:
Step 2: Run the service by using ‘Ctrl+F5’. Once the service is running, right click on the Winform project > Add Service Reference and paste the address of the service in the address bar as below:
Rename the reference name to ‘MyRef’, the proxy stub and App.config file will be created in client project. If you open the App.Config, you will find endpoint for the service uses ‘basicHttpBinding’. This is the new feature of WCF 4.0 where we have a simple configuration -- it is not necessary for us to specify endpoint, it will be automatically configured for Http transport.
Step 3: Now in the Form.cs write the following code, this code will create an object of the classes exposed by the WCF service and call method:
C#
public partial class Form1 : Form
{
//Proxy Stub Object Creation
MyRef.clsInput objIn;
MyRef.clsOutput objOp;
MyRef.MyDataServiceContractClient Proxy;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
objIn = new WCF_WindowsClient.MyRef.clsInput();
Proxy = new WCF_WindowsClient.MyRef.MyDataServiceContractClient();
}
private void brnGetData_Click(object sender, EventArgs e)
{
objIn.DbName = txtdbname.Text;
objIn.TbName = txttbname.Text;
//Method call
objOp = Proxy.GetData(objIn);
dgdata.DataSource = objOp.Ds.Tables[txttbname.Text];
}
}
VB.NET
Partial Public Class Form1
Inherits Form
'Proxy Stub Object Creation
Private objIn As MyRef.clsInput
Private objOp As MyRef.clsOutput
Private Proxy As MyRef.MyDataServiceContractClient
Public Sub New()
InitializeComponent()
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
objIn = New WCF_WindowsClient.MyRef.clsInput()
Proxy = New WCF_WindowsClient.MyRef.MyDataServiceContractClient()
End Sub
Private Sub brnGetData_Click(ByVal sender As Object, ByVal e As EventArgs)
objIn.DbName = txtdbname.Text
objIn.TbName = txttbname.Text
'Method call
objOp = Proxy.GetData(objIn)
dgdata.DataSource = objOp.Ds.Tables(txttbname.Text)
End Sub
End Class
Step 4: To test the application from the solution explorer, select multiple start up project and select start from service as well as client project. Run the project by pressing ‘F5’. Enter values, you will get following output:
Note: If you open the WF_DataService.xaml in Xml editor, you will see the following XAML Code:
<p:Activity mc:Ignorable="" x:Class="WCF_ServiceHost.WF_DataService" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:p="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:p1="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:w="clr-namespace:WCF_ServiceHost;assembly=WCF_ServiceHost" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<p:Sequence DisplayName="WFDataService" sad:XamlDebuggerXmlReader.FileName= "G:\VS2010_Practice\WCF_WF_ServiceHostClien\WCF_ServiceHost\WF_DataService.xaml">
<p:Sequence DisplayName="WFData Service Receive Reply">
<p:Sequence.Variables>
<p:Variable x:TypeArguments="p1:CorrelationHandle" Name="__Handle" />
<p:Variable x:TypeArguments="w:clsInput" Name="objInput" />
<p:Variablex:TypeArguments="w:clsOutput"Name="objOutput" />
<p:Variable x:TypeArguments="w:CDataAccess" Name="objDal" />
</p:Sequence.Variables>
<p1:Receive x:Name="__ReferenceID0" CanCreateInstance="True" DisplayName="Accept Request" OperationName="GetData" ProtectionLevel="None" ServiceContractName="MyDataServiceContract" ValueType="w:clsInput">
<p1:Receive.AdditionalCorrelations>
<p:InArgument x:TypeArguments="p1:CorrelationHandle" x:Key="ChannelBasedCorrelation">[__Handle]</p:InArgument>
</p1:Receive.AdditionalCorrelations>
<p:OutArgument x:TypeArguments="w:clsInput">[objInput]</p:OutArgument>
</p1:Receive>
<p:InvokeMethod x:TypeArguments="w:clsOutput" MethodName="RetDs" Result="[objOutput]" TargetType="w:CDataAccess">
<p:InArgument x:TypeArguments="w:clsInput">[objInput]</p:InArgument>
</p:InvokeMethod>
<p1:SendReply Request="{x:Reference __ReferenceID0}" ValueType="w:clsOutput">
<p:InArgument x:TypeArguments="w:clsOutput">[objOutput]</p:InArgument>
</p1:SendReply>
</p:Sequence>
</p:Sequence>
</p:Activity>
|
Summary: Using .NET 4.0, WF and WCF are now more integrated. It is very easy now to develop WCF service using WF declarative xaml code. WF 4.0 has introduced lots new activities using which procedural, communication and flowchart based workflow designing has been made easy.
The entire source code of this article can be downloaded over here
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @
maheshdotnet or connect with him on
LinkedIn