デフォルトでは、WCF はサービス オブジェクト プールをサポートしていません。カスタムIInstanceProviderが必要です。このシナリオでは、WCF コンテキスト モードは、WCF が IInstanceProvider から新しいオブジェクトを要求するタイミングを定義し、IInstanceProvider の動作がプールを管理します。使用状況によっては、サービスを PerInstance または PerSession に設定することが理にかなっている場合があります。
Castle Windsor、StructureMap、または MS Enterprise Library のUnityなどの実装で依存性注入コンテナーを使用している場合は、プールされたライフスタイルでコンテナーの既存の IInstanceProvider を使用できます。これらのコンテナーはすべて合理的です (ただし、個人的にはオブジェクト プールを管理する経験はあまりありません)。
個人的に選んだコンテナはキャッスル ウィンザーです。その場合、Windsor のWCF Integration Facilityとプールされたライフスタイルを使用します。
Castle.Facilites.WcfIntegraion NuGet パッケージを使用する簡単なテスト コンソール プログラムを次に示します。
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();
}
}
}
}
Castle がプールを管理する方法のすべてのルールを理解しているふりをするつもりはありませんが、プールは明らかに使用されています。出力は次のとおりです。
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