DotNetCurry Logo

Creating WCF Services using .NET 4.0 and WF 4.0

Posted by: Mahesh Sabnis , on 7/18/2009, in Category Windows Communication Foundation (WCF)
Views: 27366
Abstract: In this article we will see how to develop a WCF service using Workflow 4.0 and how to use workflow procedural and messaging activities.
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’.
4.0 Solution
Step 2: In this Solution add a new Project of type ‘Sequential Workflow Console Application’. Name this as ‘WCF_ServiceHost’.
New Project
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’.
Sequence
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:
SequenceView 
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.
WorkFlow DataService
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.
Select .NET Type
All variables declared should be displayed as below:
Variables
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.
WFDataServiceProp
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:
SelectConcreteTypeForGenericDef
The WF will be displayed as below:
WorkFlowView
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:
Parameters
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.
Parameters
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:
Change Result Prop
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:
Console Service View
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:
Browser View
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:
New Form
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:
Add Service Reference
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:
Running Service
Form with Data
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
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+
Further Reading - Articles You May Like!
Author
Mahesh Sabnis is a DotNetCurry author and Microsoft MVP having over 17 years 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). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Saket Karnik on Monday, July 20, 2009 1:17 AM
It's a gr8 article.
Comment posted by ss on Friday, August 2, 2013 1:22 PM
good article