views:

480

answers:

1

Hi,

so the store 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 configure it using the standard configuration file. The problem started when i was trying to map interface IServiceLocator to get the instance of ServiceLocator when 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()....

My question: Is there any way to tell unity (in 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.

thanks.

A: 

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!

Andrey Khataev