Labels

Wednesday, May 22, 2019

WCF Services Consumption Approach & Fault Excpetion & Throttling

Four Ways:

 - Adding Service Reference
 - Using Tool i.e. SvcUtil.exe
 - Implementing ClientBase
 - Implement Channel Factory

#1:Adding Service Reference 
 - Easy/Simple Way to Implement
 - Internal creates proxy by adding Service Reference
 - Help: http://www.topwcftutorials.net/2014/05/calling-wcf-self-hosted-console-application.html

 
#2: Using Tool i.e. SvcUtil.exe
  - Again Easy/Simple Way to Implement
  - Host Service
  - Run svctool to generate proxy - svcutil http://localhost:4321/StudentService /out:StudentServiceProxy.cs
  - Add newly created proxy class to our client application “ClientApp3″.
  - Then call WCF Service using proxy class.
  - Help: https://www.codeproject.com/Articles/786601/Ways-to-generate-proxy-for-WCF-Service

#3:Implementing ClientBase
 - Generating proxy by using ClientBase class option has an advantage
 - That it creates proxy at run time, so it will accommodate service implementation changes
 - public class StudentServiceProxy : ClientBase, IStudentService
{
public string GetStudentInfo(int studentId)
{
   return base.Channel.GetStudentInfo(studentId);
}
}
 - Help: https://www.codeproject.com/Articles/786601/Ways-to-generate-proxy-for-WCF-Service

#4: Implement Channel Factory
 - No Need to create proxy here. I.e. Channel Factory enables you to create a communication channel to the service without a proxy.
 - Cases where service is tightly bound with to the client application, we can use an interface DLL directly and with the help of a Channel Factory.
 - Channel Factory class is useful when you want to share a common service contract DLL between the client and the server.
 - ChannelFactory takes a generic parameter of the service type (it must be a service contract interface) to create a channel. 
 - Because ChannelFactory only requires knowledge of the service contract, it makes good design sense to put service/data contracts in separate assemblies from service implementations.
 - This way, you can safely distribute contract assemblies to third parties who wish to consume services, without the risk of disclosing their actual implementation.
 - Code:
BasicHttpBinding myBinding = new BasicHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress("http://localhost:3047/Service1.svc");
ChannelFactory myChannelFactory = new ChannelFactory(myBinding, myEndpoint);

IService1 instance = myChannelFactory.CreateChannel();
// Call Service.
Console.WriteLine(instance.GetData(10));

myChannelFactory.Close();
 - Help:
https://www.c-sharpcorner.com/UploadFile/ff2f08/channel-factory-in-wcf/
https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-use-the-channelfactory

Fault Exception:

Throw Fault Exception: Three Ways

namespace HelloIndigo.Lab4.Fault
{
    [ServiceContract(Namespace = "http://www.thatindigogirl.com/samples/2006/06")]
    public interface IHelloIndigoService
    {
        [OperationContract]
        [FaultContract(typeof(InvalidOperationException))]
        void ThrowSimpleFault();

        [OperationContract()]
        [FaultContract(typeof(InvalidOperationException))]
        void ThrowFaultException();

        [OperationContract]
        [FaultContract(typeof(InvalidOperationException))]
        void ThrowMessageFault();

    } 

    [ServiceBehavior(IncludeExceptionDetailInFaults=false)]
    public class HelloIndigoService : IHelloIndigoService
    {
        public void ThrowSimpleFault()
        {
            throw new FaultException(new FaultReason("Invalid operation."));
        }

        public void ThrowFaultException()
        {
            FaultException<InvalidOperationException> fe = new FaultException<InvalidOperationException> (
                                                        new InvalidOperationException("An invalid operation has occured."),
                                                        new FaultReason("Invalid operation."),
                                                        new FaultCode("Server", new FaultCode(String.Format("Server.{0}", typeof(NotImplementedException)))));
            throw fe;
        }

        public void ThrowMessageFault()
        {
            InvalidOperationException error = new InvalidOperationException("An invalid operation has occurred.");
            MessageFault mfault = MessageFault.CreateFault(
                                                        new FaultCode("Server", new FaultCode(String.Format("Server.{0}", error.GetType().Name))),
                                                        new FaultReason("Invalid operation."),
                                                        error);

            FaultException fe = FaultException.CreateFault(mfault, typeof(InvalidOperationException));

            throw fe;
        }
    }
}

Catch Fault Exception:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using HelloIndigoClient.HIServiceReferenceFault;

namespace HelloIndigoClient
{
    class Program
    {
        static void Main(string[] args)
        {
            HelloIndigoServiceClient proxy = new HelloIndigoServiceClient();

            try
            {
                proxy.ThrowSimpleFault();

            }
            catch (FaultException fe)
            {
                Console.WriteLine(fe.GetType().ToString());
                Console.WriteLine("ERROR: {0}", fe.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.GetType().ToString());
                Console.WriteLine("Normal ERROR: {0}", ex.Message);
            }
// -----------------------------------------------------------------
            try
            {
                proxy.ThrowFaultException();
            }
            catch (FaultException<InvalidOperationException> fe)
            {
                Console.WriteLine(fe.GetType().ToString());
                Console.WriteLine("ERROR: {0}", fe.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.GetType().ToString());
                Console.WriteLine("Normal ERROR:  {0}", ex.Message);
            }
            Console.WriteLine();
// -----------------------------------------------------------------

            try
            {
                proxy.ThrowMessageFault();

            }
            catch (FaultException fe)
            {
                Console.WriteLine(fe.GetType().ToString());
                Console.WriteLine("ERROR: {0}", fe.Message);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.GetType().ToString());
                Console.WriteLine("Normal ERROR:  {0}", ex.Message);
            }

            proxy.Close();
        }
    }
}

Catch Fault Exception : In Correct Order

Because FaultException<TDetail> derives from FaultException, and FaultException derives from CommunicationException, it is important to catch these exceptions in the proper order. If, for example, you have a try/catch block in which you first catch CommunicationException, all specified and unspecified SOAP faults are handled there; any subsequent catch blocks to handle a custom FaultException<TDetail> exception are never invoked.

Remember that one operation can return any number of specified faults. Each fault is a unique type and must be handled separately.

Note:
CommunicationException class has two important derived types, 

  • FaultException and 
  • Generic FaultException type.

FaultException exceptions are thrown when a listener receives a fault that is not expected or not specified in the operation contract. Usually this occurs when the application is being debugged and the service has the ServiceDebugBehavior.IncludeExceptionDetailInFaults property set to true.

FaultException exceptions are thrown on the client when a fault that is specified in the operation contract is received in response to a two-way operation (that is, a method with an OperationContractAttribute attribute with IsOneWay set to false).

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using Microsoft.WCF.Documentation;

public class Client
{
  public static void Main()
  {
    // Picks up configuration from the config file.
    SampleServiceClient wcfClient = new SampleServiceClient();
    try
    {
      // Making calls.
      Console.WriteLine("The service responded: " + wcfClient.SampleMethod(greeting));
      wcfClient.Close();
    }
    catch (TimeoutException timeProblem)
    {
      Console.WriteLine("The service operation timed out. " + timeProblem.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException<GreetingFault> greetingFault)
    {
      Console.WriteLine(greetingFault.Detail.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (FaultException unknownFault)
    {
      Console.WriteLine("An unknown exception was received. " + unknownFault.Message);
      Console.ReadLine();
      wcfClient.Abort();
    }
    catch (CommunicationException commProblem)
    {
      Console.WriteLine("There was a communication problem. " + commProblem.Message + commProblem.StackTrace);
      Console.ReadLine();
      wcfClient.Abort();
    }
  }
}


Throttling:

Throttling is mainly derived from the throughput keyword. Throughput means that the work performs or can be performed in a given specific time interval.

The throughput of any WCF Service mainly depends on three things:

  • maxConcurrentCalls - Controls client can create a maximum number of 2 concurrent calls to communicate with WCF Service
  • maxConcurrentInstances - Controls client can create a maximum number of 2 concurrent instances to communicate with WCF Service
  • maxConcurrentSessions - Controls client can create a maximum number of 2 concurrent sessions to communicate with WCF Service
It means the client only creates maximum number for the concurrent calls, sessions and instances as per throttling behavior. If the client crosses the limit, then the Service will not allow doing this.

To  control such factors we have to use throttling. In throttling, you can specify it in  two ways:  In app.config or At hosting time programmatically.

Config File:

<behaviors>
  <serviceBehaviors>
    <behavior name="throttlingBehavior">
      <serviceThrottling
        maxConcurrentCalls="2"
        maxConcurrentInstances="2"
        maxConcurrentSessions="2"/>
    </behavior>
  </serviceBehaviors>
</behaviors>

Programmatically

ServiceHost host = new ServiceHost(typeof(ThrottlingInWCF.ThrottlingInWCF));

ServiceThrottlingBehavior stb= new ServiceThrottlingBehavior();
stb.MaxConcurrentCalls=2;
stb.MaxConcurrentInstances=2;
stb.MaxConcurrentSessions=2;

host.Description.Behaviors.Add(stb);
host.Open();
Console.Write("Service started...");
Console.Read();

Hope this helps...

Regards,
Arun Manglick


No comments:

Post a Comment