简体   繁体   中英

Accessing the WCF singleton in IIS

I have a service (called MainService ) that has instanceContextMode = single .

I also have a another service, this service has to talk to the single instance of the above service ( OtherService calls function in MainService ).

The way I decided to do this was like so:

public class OtherService
{
   ...
   MainService.DoSomeFunction();
   ...
}

and in the MainService class:

public class MainService
{
   private ManualResetEvent manualEventInCtor = new ManualResetEvent(false);
   private static MainService _theInstance = null;
   private static MainService TheInstance
   {
       get
       {
           if(_theInstance == null)
           {
                MainService.IMainContract dummyClient = new MainService.MainContractClient();
                dummyClient.function();
                manualEventInCtor.WaitOne();
           }
           return _theInstance;
       }
       set
       {
           _theInstance = value;
       }
   }
   ...
   public MainService()
   {
       ...
       TheInstance = this;
       manualEventInCtor.set();
       ...
   }

   public static void DoSomeFunction()
   {
       TheInstance.SomeFunction();
   }
   ...
}

I end up having to have the TheInstance getter create a dummy call to the service so that IIS will create the instance (this is needed only if the service instance has yet to be created by IIS). This is obviously a convoluted hack, and I want to know if there is a normal way.

I can't do a normal singleton pattern because I am hosting in IIS, and IIS won't know how to create the service instance from some function (eg GetInstance ).

EDIT 1

I don't want OtherService to talk to the MainService like any other service, because then MainService would have to define DoSomeFunction as a service operation, and anybody could call the function DoSomeFunction. DoSomeFunction is meant for OtherService, not for just any client.

Just don't have a single instance service. Move all state into a different class and have your service be stateless. The stateless service can obtain the singleton instance and use it.

By moving all statefulness into your own code you control when instances are created. You can force it to be created.

Here's a sketch:

class MySingleton {
 //You can use any lazy initialization logic you like
 //I just used a static initializer as an example
 public static readonly MySingleton Instance = new ...();

 //Move all static data into this class
 //WCF never has to instantiate this class
 //Use it from anywhere you like
}

class MyWcfService {
 //This WCF service has no state
 //Therefore it does not need single instance mode
 //Any instancing mode will do
 //No one except WCF will ever need to use this class
 public void SomeServiceMethod() {
  MySingleton.Instance.DoSomething();
 }
}

This has nothing to do with the question: Stateful web-services and web-apps are to be avoided. You must assume that the application is killed at any time (for example a power failure, crash, bug, ...). Also, you need a high availability solution which usually involves instantiating the application multiple times.

  1. First thing do not code your own singleton as your are setting InstanceContextMode to Single on service. WCF will create only one for serving all requests.

  2. From other service call wcf service as any other service , like using WCF client proxy.

  3. You do not need to bother is IIS has created instance or not it will create instance on first request.

  4. If you bother about performance of calling main service from another service if they are on same machine same app in IIS , consider using nettcp binding or net named pipe binding.

I'll be frank and say that I think that you probably have some design problems; however, what you're asking is not impossible.

Implement this class :

public static class Singleton<T> where T : new()
{
    public static event EventHandler<SingletonChangedEventArgs> SingletonChangedEvent;
    public static T Instance { get { return instance; } }
    private static T instance = new T();
    public static void SetSingleton(T newInstance)
    {
        T temp = instance;
        instance = newInstance;
        if (SingletonChangedEvent != null)
            SingletonChangedEvent(instance, new SingletonChangedEventArgs { PreviousInstance = temp});
    }
}

and this one as well :

public class SingletonChangedEventArgs : EventArgs
{
    public object PreviousInstance { get; set; }
}

Make your main service like any other service like such:

[ServiceBehavior( InstanceContextMode = InstanceContextMode.Single)]
public class MyService : IMyService
{
    public MyService()
    {
        //Pay attention to this, the rest of this class is fluff.
        Singleton<MyService>.SetSingleton(this);
    }
    public String Hello(String Name)
    {
        return "Hello " + Name;
    }

    public Person GetPerson()
    {
        return new Person() { Age = 21 };
    }
}

I strongly advise that you subscribe to the SingletonChangedEvent and write logic for if the singleton is changed because the point of a singleton is that it can only be instantiated once, if your singleton manages a state then that state can be changed it can have some major repercussions.

A good way to do this is to keep a reference to the singleton in any client code and subscribe with the event. If the address of the singleton instance is different from the one the client code has the client code maintains the ability to change it back.

This will allow you to unit test your singleton but also allow you to use it as a singleton in addition to allowing you to run it normally.

Anyway, if you ever want to access your singleton its easy :

Singleton<MyService>.Instance.Hello("Aelphaeis");

I feel that this solution is a lot cleaner than what you're currently doing; however, I still think you have design issues and you probably need to consider changing the structure of your program.

PS/tl;dr: In case it wasn't clear. This is bad design.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM