Friday, October 10, 2014

Namespace Property in WCF

We are familiar with Service contract,Operation contract and Data contract in WCF. Let us see the importance of Name space in WCF. The namespace property specifies an XML-namespace. By default, the name of a service contract is the name of the interface. Its default namespace is "http://tempuri.org", and each operation’s action is "http://tempuri.org/contractname/methodname".
In operation contract if namespace is not present and user consume the WCF service through WSDL ( by adding service reference) , then XML present in proxy class generated by default some name space. But the thing changes when we host the service by self hosting. Here I am sharing my experience.
Let test with complex data type ,so that we will know about the importance of namespace. Following class is the data contract of Customer. Here I used the Entity framework code first approach.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.Serialization;

namespace TestWCFHost
{
[Table("L_CUSTOMER_DETAILS")]
[DataContract]
public class Customer
{
[Key]
[Column("CUST_ID")]
[DataMember]
public int CustomerId { get; set; }

[Column("CUST_FIRST_NAME")]
[DataMember]
public string FirstName { get; set; }

[Column("CUST_MIDDLE_NAME")]
[DataMember]
public string MiddleName { get; set; }

[Column("CUST_LAST_NAME")]
[DataMember]
public string LastName { get; set; }

[Column("CUST_AGE")]
[DataMember]
public int Age { get; set; }
}
}
Following  image is the Table L_CUSTOMER_DETAILS in SQL DB.



The Service contact interface as following
using System.Collections.Generic;
using System.ServiceModel;

namespace TestWCFHost
{
[ServiceContract]
public interface IProd
{
[OperationContract]
List<Customer> GetCustomerList();

[OperationContract]
string GetMessage();
}
}
The Service class that implement the service contract as following.
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;

namespace TestWCFHost
{
internal class WCFServ : IProd
{
public List<Customer> GetCustomerList()
{
List<Customer> lst;
try
{
using (var obLayer = new DataLayer())
{
lst = obLayer.Customers.ToList();
}
}
catch (Exception ex)
{
throw new FaultException(ex.Message);
}
return lst;
}

public string GetMessage()
{
return "Hello " + DateTime.Now.ToLongTimeString();
}
}
}
The program class that host the service by self hosting in a console application as following.
using System;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace TestWCFHost
{
internal class Program
{
private static void Main(string[] args)
{
var basrUri = new Uri("http://localhost:4300/WCFService");
var host = new ServiceHost(typeof(TestWCFHost.WCFServ), basrUri);
var basicHttpBinding = new BasicHttpBinding();
var mBehave = new ServiceMetadataBehavior { HttpGetEnabled = true };
host.Description.Behaviors.Add(mBehave);
host.AddServiceEndpoint(typeof(IProd), basicHttpBinding, basrUri);
host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
host.Open();
Console.Read();
}
}
}
Now the Service is ready. Start the Service by start the console application. And we need the reference of Customer Class and service contract “Iprod “ in the client end to consume the service. The class that consume the service as following. This is also a console application.
using System;
using System.ServiceModel;

namespace TestWCFClient
{
internal class Program
{
private static void Main(string[] args)
{
try
{
var instanceFactory = new ChannelFactory<IProd>(new BasicHttpBinding(),
new EndpointAddress(new Uri("http://localhost:4300/WCFService")));

instanceFactory.Open();
var customeChnl = instanceFactory.CreateChannel();

var objclient = customeChnl.GetCustomerList();

foreach (var customer in objclient)
{
Console.WriteLine(customer.FirstName);
}

Console.Read();
}
catch (Exception ex)
{
Console.Write(ex.Message);
Console.Read();
}
}
}
}
We are trying to use the “GetCustomerList” , you will notice that there are no records present as customer , though you will see 6 records are there in DB table. Confused??
Let us try adding Namespace and again test the client.
namespace TestWCFHost
{
[Table("L_CUSTOMER_DETAILS")]
[DataContract(Namespace = "http://radhamadhab/wcftest/2014/10/10")]
public class Customer
{

Now again refer the same data contract in client end and again test, you will see “GetCustomerList” method returns 6 records.
Reason:
When user try to consume the service through WSDL (Add service reference), Then there is by default NameSpace is created. But when we try by self hosting then user needs to implement the Namespace by own.
It helps to serialize and de-serialize the object while passing message between client and server.
Except that using Name space helps in detecting Versioning like Contract changes,Address changes,Binding changes and Implementation changes.
Tips:

  • Always try to make habit of using Namespace.
  • Try to host the WCF service by self hosting, so that number of things you will know which you could not by using IIS hosting.
  • Here if I would try with simple datatype (intger,string, etc) method , then there will be no issue of not using namespace. So please explore with complex datatype always.

Tuesday, September 30, 2014

Asynchronous operation in WCF using IAsyncResult

The WCF service operations can be implemented asynchronously or synchronously. Clients can consume service operations synchronously or asynchronously.

There are three ways to implement asynchronous operations
  • IAsyncResult asynchronous
  • Task-based asynchronous
  • Event-based asynchronous

Here sample code is given for IasyncResult asynchronous operation.

Service Implementation


Asynchronous operations require Begin and End methods.
using System;
using System.ServiceModel;

namespace WCF
{
[ServiceContract]
public interface IMessageService
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetMessage(string message, AsyncCallback callback, object state);

string EndGetMessage(IAsyncResult asyncResult);
}
}

In the service contract, the BeginGetMessage method takes a parameter, a callback object, and a state object, and returns an IasyncResult, while EndGetMessage method takes an IasyncResult and returns the value. Since the service operation returns IasyncResult, hence needs to be implemented first.

using System;
using System.Threading;

namespace WCF
{
public class AsyncResult IAsyncResultIDisposable
{
private AsyncCallback _callback;
private object _state;
private ManualResetEvent _manualResetEvent;

public AsyncResult(AsyncCallback callback, object state)
{
_callback = callback;
_state = state;
_manualResetEvent = new ManualResetEvent(false);
}

public bool IsCompleted
{
get return _manualResetEvent.WaitOne(0, false); }
}

public WaitHandle AsyncWaitHandle
{
get return _manualResetEvent; }
}

public object AsyncState
{
get return _state; }
}
public ManualResetEvent AsyncWait
{
get return _manualResetEvent; }

}
public bool CompletedSynchronously
{
get return false; }
}

public void Completed()
{
_manualResetEvent.Set();
if (_callback != null)
_callback(this);
}

public void Dispose()
{
_manualResetEvent.Close();
_manualResetEvent = null;
_state = null;
_callback = null;
}
}
}

AsyncResult implements IasyncResult that has some properties like AsyncCallback, state object and ManualResetEvent, which handle waiting for asynchronous operation. IsCompleted is used to specify that the operation is completed. In the Complete() method ManualResetEvent has been set, which means that the event is signaled now and any other awaiting thread can follow.
For MessageServiceOperation, MessageAsyncResult is there which just inherit from AsyncResult to add Message property.
public class MessageAsyncResult : AsyncResult
{
public string Message { get; private set; }
public MessageAsyncResult(string message, AsyncCallback callback, object state)
: base(callback, state)
{
Message = message;
}
}
Now implementing the service operations

public class MessageService : IMessageService
{
public IAsyncResult BeginGetMessage(string message, AsyncCallback callback, object state)
{
var replyMessage = string.Format("{0}{1}""Server says :", message);
var messageAsyncResult = new MessageAsyncResult(replyMessage, callback, state);
ThreadPool.QueueUserWorkItem(CompleteProcess, messageAsyncResult);

return messageAsyncResult;
}

private void CompleteProcess(object state)
{
var messageAsyncResult = state as MessageAsyncResult;
messageAsyncResult.Completed();
}

public string EndGetMessage(IAsyncResult asyncResult)
{
var messageAsyncResult = asyncResult as MessageAsyncResult;
messageAsyncResult.AsyncWait.WaitOne();
return messageAsyncResult.Message;
}
}

In the BeginGetMessage () method an instance of MessageAsyncResult is created by passing appropriate parameters to it then call ThreadPool.QueueUserWorkItem() method to add method to waiting queue for execution. The method CompleteProcess() will run whenever a thread becomes available and it finally call the Completed() method to release the thread and allow the other operations to execute. In the EndGetMessage () method, In WaitOne() method, wait for ManualResetEvent until the current execution ends then return the Result property.


Service Hosting


Here console application will be used as a server, to host service.

class Program
{
static void Main(string[] args)
{
var svcHost = new ServiceHost(typeof(MessageService));
Console.WriteLine("Available Endpoints :\n");
svcHost.Description.Endpoints.ToList().ForEach(endpoints =>Console.WriteLine(endpoints.Address.ToString()));
svcHost.Open();
Console.ReadLine();
}
}

And the configuration for that,


<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="WCF.Asynchronous.Services.MessageService">
<endpoint address="" binding="basicHttpBinding" contract="WCF.Asynchronous.Services.IMessageService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/WCF.Asynchronous.Services/" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>

Client

Here is given a simple console app to consume this service,

public class Program
{
static void Main(string[] args)
{
var channelFactory = new ChannelFactory<IMessageService>("MessageServiceEndpoint");
var proxy = channelFactory.CreateChannel();
var message = Console.ReadLine();
while (message.Equals("exit"StringComparison.CurrentCultureIgnoreCase) == false)
{
proxy.BeginGetMessage(message, ClientCallBack, proxy);
message = Console.ReadLine();
}
}

private static void ClientCallBack(IAsyncResult ar)
{
var res = ar.AsyncState as IMessageService;
if (res != null)
{
var message = res.EndGetMessage(ar);
Console.WriteLine(message);
}
}
}

And the configuration file for this is,
<configuration>
<system.serviceModel>
<bindings />
<client>
<endpoint address="http://localhost/WCF.Asynchronous.Services/"
binding="basicHttpBinding" contract="WCF.Asynchronous.Services.IMessageService"
name="MessageServiceEndpoint">
</endpoint>
</client>
</system.serviceModel>
</configuration>