Search code examples
wcfwcf-bindingwcf-restwebhttpbinding

WCF reliability issues


Trying to test the reliability of my wcf services. I am calling a wcf service within a loop from the client side. The wcf rest service (webhttpbinding) does some data processing and inserts records into a database. The entire operation is done within a transaction.

I find that out of about 60 messages (60 times the service being called from inside a loop) only 40 are going through to the db if I set my InstanceContextMode to PerCall. There are no errors no exceptions. The messages are just being dropped.

If I set the InstanceContextMode to Single then I see all messages getting to the db. Is the InstanceContextMode.Percall being lossy the expected behavior? Also, I do not have concurrency set. Any clarifications would be greatly helpful. Added the code. Using MySQL as the db...

EDIT My bad - I just noticed that I get an exception on the server side - {"Deadlock found when trying to get lock; try restarting transaction"}

This is because there is another transaction invoked on the same records while a transaction is under progress. Fixing it by restarting the transaction if it fails once.

The service

    [WebInvoke(UriTemplate = "employee", Method = "POST")]
    public long AddNewEmployee(EmployeeEntity newEmployee)
    {
        EmployeeRepository eRep = new EmployeeRepository();
        return eRep.AddNewEmployee(newEventEntity);                     
    }

The repository class constructor initializes the object context

    public EmployeeRepository()
    {
        bd = new EmployeeEntity();
    }

Code - The service call

    //bd is the object context 
    //there are two tables 
    internal long AddNewEmployee(EmployeeEntity newEmployee)
    {
        bool tSuccess = false;
        using (TransactionScope transaction = new TransactionScope())
        {
            try
            {
                //get existing employees
                var existingEmployees = from employee
                                        in bd.employees select employee;


                List<employee> returnedEmployees = new List<employee>();                    

                //Do some processing 
                returnedEmployees = DoSomeprocessing(existingEmployees);

                //Insert returned employees as updates
                foreach (employee e in returnedEmployees)
                {
                    bd.employees.AddObject(e);
                }

                bd.SaveChanges();
                returnedEmployees.Clear();

                //add to second table 
                bd.otherdetails.AddObject(newEmployee);
                bd.SaveChanges();


                //Transaction Complete                        
                transaction.Complete();
                tSuccess = true;

            }
            catch (Exception e)
            {
                //return something meaningful 
                return -1;                    
            }
        }

        if (tSuccess)
        {
            //End Transaction
            bd.AcceptAllChanges();
            return 200;
        }            
        else
        {
           return -1;
        }

    }

The client side just calls the service in a loop


Solution

  • I highly suggest adding global exception handling for any WCF. It has helped me save many hours of debugging and will catch any unhandled exceptions. It's a little bit more involved than global.ascx in ASP.NET

    Step 1 - Implement IErroHander and IServiceBehavior

    Notice inside HandleError I'm using Enterprise Library to handle the exception. You can use your custom implementation here as well.

     public class ErrorHandler : IErrorHandler, IServiceBehavior
            {        
                public bool HandleError(Exception error)
                {
                    // Returning true indicates you performed your behavior.
                    return true;
                }
    
    
                public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
                {            
                    // Log Exception
    
                    ExceptionPolicy.HandleException(error, "ExceptionPolicy");
    
                    // Shield the unknown exception
                    FaultException faultException = new FaultException(
                        "Server error encountered. All details have been logged.");
                    MessageFault messageFault = faultException.CreateMessageFault();
    
                    fault = Message.CreateMessage(version, messageFault, faultException.Action);
                }
    
                private IErrorHandler errorHandler = null;
    
                public ErrorHandler()
                {
    
                }
    
                public ErrorHandler(IErrorHandler errorHandler)
                {
                    this.errorHandler = errorHandler;
                }
    
                public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
                {
                    foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
                    {
                        ChannelDispatcher cd = cdb as ChannelDispatcher;
    
                        if (cd != null)
                        {
                            cd.ErrorHandlers.Add(new ErrorHandler());
                        }
                    }
                }
    
                public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
                {
                    foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
                    {
                        ChannelDispatcher cd = cdb as ChannelDispatcher;
    
                        if (cd != null)
                        {
                            cd.ErrorHandlers.Add(new ErrorHandler());
                        }
                    }
                }
    
                public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
                {
    
                }
            }
    
        }
    

    Step 2 - Create Error Element

    public class ErrorHandlerElement : BehaviorExtensionElement
        {
            protected override object CreateBehavior()
            {
                return new ErrorHandler();
            }
    
            public override Type BehaviorType
            {
                get
                {
                    return typeof(ErrorHandler);
                }
            }
        }
    

    Step 3 - Add Element to web.config

    <serviceBehaviors>
            <behavior>                              
              <ErrorHandler />
            </behavior>
          </serviceBehaviors>