Mr. T.
Mr. T.

Reputation: 1345

Choosing a WCF Instance Management

We are designing a WCF web service on IIS with the following characteristics:

Which of the 3 Instance Management contexts (PerCall, PerSession, Single) would you recommend and why? Which instance enables us to manage a pool of available objects that would do the work?

Upvotes: 1

Views: 365

Answers (1)

ErnieL
ErnieL

Reputation: 5801

Out of the box, WCF does not support a service object pool. You need a custom IInstanceProvider. In this scenario the WCF context mode would define when WCF requests a new object from the IInstanceProvider and the IInstanceProvider behavior would manage the pool. Setting the service to either PerInstance or PerSession could make sense depending on the usage.

If you are using a Dependency Injection Container in your implementation such as Castle Windsor, StructureMap, or MS Enterprise Library's Unity then you can use the container's exsiting IInstanceProvider with a pooled lifestyle. All of those containers are reasonable (although I don't personally have much experience with having them manage object pools).

My personal choice of container is Castle Windsor. In that case you would use Windsor's WCF Integration Facility and a pooled lifestyle.

Here's a quick test console program that uses the Castle.Facilites.WcfIntegraion NuGet package.

using Castle.Facilities.WcfIntegration;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Threading.Tasks;

namespace WCFWindsorPooledService
{
    [ServiceContract]
    public interface ISimple
    {
        [OperationContract]
        void Default(string s);
    }

    public class SimpleService : ISimple
    {
        private static int CurrentIndex = 0;
        private int myIndex;

        public SimpleService()
        {
            // output which instance handled the call.
            myIndex = System.Threading.Interlocked.Increment(ref CurrentIndex);
        }

        public void Default(string s)
        {
            Console.WriteLine("Called #" + myIndex);
            System.Threading.Thread.Sleep(5);
        }
    }

    class PooledService
    {
        static void Main(string[] args)
        {
            Console.WriteLine("\n\n" + System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType.Name);

            // define mapping of interfaces to implementation types in Windsor container.
            IWindsorContainer container = new WindsorContainer();
            container.AddFacility<WcfFacility>()
                     .Register(Component.For<SimpleService>().LifeStyle.PooledWithSize(2, 5));

            var host = new Castle.Facilities.WcfIntegration.DefaultServiceHostFactory()
                                                           .CreateServiceHost(typeof(SimpleService).AssemblyQualifiedName,
                                                                              new Uri[] { new Uri("http://localhost/Simple") });
            host.Open();

            ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(host.Description.Endpoints[0]);

            List<Task> tasks = new List<Task>();
            for (int i = 0; i < 20; i++)
            {
                tasks.Add(Task.Run(() =>
                {
                    ISimple proxy = factory.CreateChannel();
                    proxy.Default("Hello");

                    ((ICommunicationObject)proxy).Shutdown();
                }));
            }

            Task.WaitAll(tasks.ToArray());

            ((ICommunicationObject)host).Shutdown();
            container.Dispose();
        }
    }

    public static class Extensions
    {
        static public void Shutdown(this ICommunicationObject obj)
        {
            try
            {
                obj.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Shutdown exception: {0}", ex.Message);
                obj.Abort();
            }
        }
    }
}

I'm not going to pretend to understand all the rules of how Castle manages a pool, but a pool is clearly being used. The output is:

PooledService
Called #1
Called #5
Called #2
Called #3
Called #4
Called #6
Called #7
Called #8
Called #7
Called #4
Called #2
Called #5
Called #1
Called #10
Called #6
Called #9
Called #4
Called #7
Called #1
Called #9

Upvotes: 3

Related Questions