R – Unity framework with configuration file and singleton classes

ioc-containersingletonunity-container

So the story goes like this:

I have singleton class (a.k.a ServiceLocator) which you can get the instance using the "CreateInstance()" method. At the same time, we added Unity into our application and configured it using the standard configuration file.

The problem started when i was trying to map the interface IServiceLocator to get the instance of ServiceLocator and register it in the unity configuration file. As you probably guesses, ServiceLocator DOES NOT have a public constructor (i.e, singleton), hence, unity cannot create it when I do unity.resolve<IServiceLocator>()….

My question: Is there any way to tell Unity (through the configuration file) to use CreateInstance() instead of trying to execute the default constructor? If not, if you have any other idea what else can be done, I would appreciate to hear it.

Please don't suggest me to change the constructor to public, assume I can't do it for now.

Best Answer

You'd better create manually UnityContainer and inject it into your service locator:

public class ServiceLocator 
{
    public static void SetServiceLocatorProvider(IServiceLocator serviceLocator)
    {
        Instance = serviceLocator;
    }

    public static void SetServiceLocatorProvider(Func<IServiceLocator> serviceLocator)
    {
        Instance = serviceLocator();
    }

    public static IServiceLocator Instance { get; private set; }
}


public class ServiceLocatorContainer : IServiceLocator
{
    private readonly IUnityContainer _unityContainer;

    public ServiceLocatorContainer(IUnityContainer unityContainer)
    {
        _unityContainer = unityContainer;
    }
//skipped for simplicity sake
 }


    public abstract class ApplicationControllerBase  
    {


        /// <summary>
        /// Создать экземпляр Unity контейнера
        /// </summary>        
        private IUnityContainer CreateContainer()
        {
            return new UnityContainer();
        }

        protected virtual void ConfigureContainer()
        {

            RegisterTypeIfMissing(typeof(IServiceLocator), typeof(ServiceLocatorContainer), true);
            RegisterTypeIfMissing(typeof(ISharedAssemblyInitializer), typeof(SharedAssemblyInitializer), true);

            ServiceLocator.SetServiceLocatorProvider(() => this.Container.Resolve<IServiceLocator>());

        }


    protected void RegisterTypeIfMissing(Type fromType, Type toType, bool registerAsSingleton)
    {

        if (Container.IsTypeRegistered(fromType))
        {

        }
        else
        {
            if (registerAsSingleton)
            {
                Container.RegisterType(fromType, toType, new ContainerControlledLifetimeManager());
            }
            else
            {
                Container.RegisterType(fromType, toType);
            }
        }
    }


        public virtual void Run()
        {
            this.Container = CreateContainer();
            this.ConfigureContainer();
        }

        public IUnityContainer Container { get; private set; }
}

this is a simplified version of prism extension to unity. see more detailed sources of modulesmanager class in prism. app starts with Run method. You create container and register your servicelocator in it. Wile registering, UnityContainer creates instance of it and pass itself into ctor:

public ServiceLocatorContainer(IUnityContainer unityContainer)
{
    _unityContainer = unityContainer;
}

then, using SetServiceLocatorProvider method you assign to Instance property instance of servicelocator class, that has reference to unity container within. Such solution also gives abstraction of concrete DI container. And you can reference to your service locator from anywhere in your code. 1) Injectiong into constructor

public class RLoginABS
{

    IServiceLocator serviceLocator;

    public RLoginABS(IServiceLocator serviceLocator)
    {
        this.serviceLocator = serviceLocator;
    }

    public void Login(string user, string password)
    {            
        REnvironmentRWS environment = REnvironmentRWS.Current;            
        serviceLocator.RegisterInstance<IEnvironmentRWS>(environment as IEnvironmentRWS);
    }

}

or using static class:

shellViewModel = ServiceLocator.Instance.Resolve<IShellViewModel>();

ps: IServiceLocator interface, which is an abstraction of concrete implementation of DI container:

public interface IServiceLocator
{
    void Register<TInterface, TImplementor>() where TImplementor : TInterface;
    void Register(Type TInterface, Type TImplementor);
    void RegisterAsSingleton<TInterface, TImplementor>() where TImplementor : TInterface;
    T Resolve<T>();
}

its methods wraps concrete container's methods, for example:

public void Register(Type TInterface, Type TImplementor)
{
    _unityContainer.RegisterType(TInterface, TImplementor);
}

Hope - it helps!

Related Topic