How to recreate a WCF Service as Singleton instance with SimpleInjector (using Tridion)

733 Views Asked by At

I'm using the IoC Container SimpleInjector. I know that Singletons shouldn't be recreated since that's not their purpose but my problem is with WCF and when it enters into Faulted state which according to several readings it cannot be recovered and a new instance needs to be created.

I have a class named CoreServiceService which uses two WCF Services.

  1. CoreService
  2. ECLService

I want those services to be singleton since I'll be making lots of calls to CoreServiceSession and creating those WCF Service is too expensive and takes a lot of times, after the creation, they are much faster.

I'm registering them like this:

container.Register(() => new SessionAwareEclServiceClient(binding, eclServiceRemoteAddress), Lifestyle.Singleton);

container.Register(() => new SessionAwareCoreServiceClient(binding, coreServiceRemoteAddress), Lifestyle.Singleton);

container.Register(typeof(ICoreServiceSession), typeof(CoreServiceSession), Lifestyle.Scoped);

My problem is that while using ECLService if something cannot be retrieved is enters into Faulted connection, In that case, I call .Abort() and close the connection. But the next time I call my service ECLService WCF service keeps being in the Faulted state (since it's a singleton) so I need a way to recreate the connection.

I tried with something like:

coreServiceSession.EclServiceClient = (SessionAwareEclServiceClient)container.GetInstance(typeof(SessionAwareEclServiceClient));

But, of course, it gives me the same instance.

I also tried using this initializer:

container.RegisterInitializer<ICoreServiceSession>(coreServiceSession =>
{
    if (coreServiceSession.EclServiceClient.State == CommunicationState.Faulted)
    {
        coreServiceSession.EclServiceClient.Abort();
        coreServiceSession.EclServiceClient = null;
        coreServiceSession.EclServiceClient = (SessionAwareEclServiceClient)container.GetInstance(typeof(SessionAwareEclServiceClient));
    }
}

Same thing and I tried to use instead of container.GetInstance, this:

coreServiceSession.EclServiceClient = new SessionAwareEclServiceClient(binding, eclServiceRemoteAddress);

Same things. Any ideas/options?

It there any way to force to get a new instance in this case?

UPDATE

This is part of the class CoreServiceSession:

public class CoreServiceSession : ICoreServiceSession
{
    public CoreServiceSession(ISessionAwareCoreService sessionAwareEclServiceClient, SessionAwareCoreServiceClient sessionAwareCoreServiceClient)
    {
        EclServiceClient = sessionAwareEclServiceClient;
        CoreServiceClient = sessionAwareCoreServiceClient;
    }

    public ISessionAwareCoreService EclServiceClient { get; set; }

    public SessionAwareCoreServiceClient CoreServiceClient { get; set; }

    public string CreateOrGetStubUris(string eclItemUri)
    {
        var stubInfo = EclServiceClient.CreateOrGetStubUris(new List<string> { eclItemUri }).FirstOrDefault();
    }
}

Thanks in advance. Guillermo

1

There are 1 best solutions below

1
On

@ScottHannen already gave the answer in his comment: do not register channels as singletons: they are not expensive to create, only the channel factories are.

As a matter of fact, you shouldn't inject your WCF client objects into constructors at all. Injecting them into a constructor implies that they are a useful abstraction that can be used to intercept, mock or replace, while the class using such client is typically strongly coupled to WCF.

So instead of injecting them into a constructor, let the consumer create them internally using the ChannelFactory. Even such ChannelFactory typically doesn't have to be injected, you can just new it up in a private static field.

This is how your CoreServiceSession might look like:

public class CoreServiceSession : ICoreServiceSession
{
    private static readonly ChannelFactory factory =
        new ChannelFactory<ISessionAwareCoreService>(myBinding, myEndpoint);

    public string CreateOrGetStubUris(string eclItemUri)
    {
        var client = factory.CreateChannel();

        try
        {
            return EclServiceClient.CreateOrGetStubUris(
                new List<string> { eclItemUri }).FirstOrDefault();
        }
        finally
        {
            try
            {
                ((IDisposable)client).Dispose();
            }
            catch
            {
                // We need to swallow exceptions thrown by Dispose. 
                // See: https://marcgravell.blogspot.com/2008/11/dontdontuse-using.html
            }
        }
    }
}