2

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

Reza Ahmadi
  • 862
  • 2
  • 11
  • 23
polonskyg
  • 4,269
  • 9
  • 41
  • 93
  • Can you add a method to your instance to force a an update/refresh of some sort? Can you share your client code? – Stinky Towel Oct 09 '17 at 18:12
  • @StinkyTowel Added part of the code – polonskyg Oct 09 '17 at 18:20
  • Can you add the code that creates the concrete client of **ISessionAwareCoreService sessionAwareEclServiceClient**? – Stinky Towel Oct 09 '17 at 18:24
  • That's created by the IoC Container – polonskyg Oct 09 '17 at 19:36
  • 2
    [Here's an interesting post.](https://stackoverflow.com/questions/1681787/wcf-client-proxy-initialization/1682038#1682038). To summarize - it says make the `ChannelFactory` a singleton, because that's the expensive part. But don't make the client a singleton. – Scott Hannen Oct 09 '17 at 19:43

1 Answers1

1

@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
            }
        }
    }
}
Steven
  • 166,672
  • 24
  • 332
  • 435
  • 1
    I second this approach and have used it successfully many times. However, keeping in the spirit of IoC, what I've usually done is inject a Func() which is registered with the IoC and that lambda takes care of using a singleton ChannelFactory. Also extension methods can be written to do a CleanClose of the client (the part in the Try-Finally block – KMeda Oct 11 '17 at 19:03