Как разрешить именованные экземпляры из LightCore ServiceLocator? - PullRequest
1 голос
/ 06 сентября 2011

В последнее время я немного поиграюсь с инструментом IoC LightCore. Поскольку в последней версии именованные экземпляры больше не разрешены, тем не менее, шаблон ServiceLocator поддерживается (?).

Одной из важнейших функций, которые мне нужны от ServiceLocator, является разрешение именованных экземпляров. Моей первой идеей было переопределить метод DoGetInstance и реализовать мой собственный код.

Мой последний обходной путь состоял в том, чтобы получить экземпляр по его typename

_testInstanceKey = "My.Namespace.MyType, MyAssembly";
IMyType type = locator.GetInstance(typeof(IMyType), _testInstanceKey)

protected override object DoGetInstance(Type serviceType, string key)
{
    return _container.ResolveAll(serviceType)
             .Where(x => x.GetType() == Type.GetType(key,true,true))
             .FirstOrDefault();
}

Это работает, но зачем мне ServiceLocator, если я передаю свое имя типа?!

Есть ли у кого-нибудь какие-либо предложения, как я мог бы обойти этот недоделанный ServiceLocator?

Ответы [ 2 ]

2 голосов
/ 06 сентября 2011

Вы можете решить эту проблему, введя интерфейс IMyTypeFactory и разрешив приложению зависеть от этого интерфейса:

// Factory interface:
public interface IMyTypeFactory
{
    IMyType GetByName(string name);
}

// Implementation in the composition root:
public class MyTypeFactory :
    Dictionary<string, Func<IRequestHandler>>, IMyTypeFactory
{
    public IMyType GetByName(string name) { return this[name](); }
}

// Registration
var factory = new MyTypeFactory
{
    { "foo", () => new MyType1() },
    { "bar", () => new MyType2() },
    { "boo", () => new MyType3() },
};

builder.Register<IMyTypeFactory>(c => factory);

Как я уже говорил в комментариях, постарайтесь отойти от использования антишаблона Service Locator. Это улучшит тестируемость и ремонтопригодность вашего приложения. При использовании шаблона внедрения зависимостей (DI) вы не вызываете контейнер напрямую, и нет способа напрямую запросить именованный экземпляр. При применении DI вам придется изменить способ настройки контейнера или применить правильные абстракции в вашем приложении (как показано IMyTypeFactory).

0 голосов
/ 15 сентября 2011

Предполагая, что вы не можете изменить способ, которым платформа вызывает локатор, и предполагая, что платформа использует Common Service Locator, используемый в качестве интерфейса между вашим контейнером и самим собой, вы можете добавить эту функциональность в пользовательскую реализацию IServiceLocator:

// This adapter wraps the CSL LightCoreAdapter of LightCore itself.
public class LightCoreServiceLocatorAdapter : IServiceLocator
{
    private readonly LightCoreAdapter container;

    public LightCoreServiceLocatorAdapter(IContainer container)
    {
        // You need a reference to LightCore.CommonServiceLocator.dll.
        this.container = new LightCoreAdapter(container);
    }

    public IEnumerable<TService> GetAllInstances<TService>()
    {
        return this.container.GetAllInstances<TService>();
    }

    public IEnumerable<object> GetAllInstances(Type serviceType)
    {
        return this.container.GetAllInstances(serviceType);
    }

    public TService GetInstance<TService>(string key)
    {
        if (key == null)
        {
            return this.container.GetInstance<TService>(null);
        }
        else
        {
           // This is custom logic
           this.container.GetInstance<INamedFactory<TService>>().GetByKey(key);
        }
    }

    public TService GetInstance<TService>()
    {
        return this.container.GetInstance<TService>();
    }

    public object GetInstance(Type serviceType, string key)
    {
        if (key == null)
        {
            return this.container.GetInstance(serviceType);
        }
        else
        {
            // This is custom logic
            var facType = typeof(INamedFactory<>).MakeGenericType(serviceType);
            var factory = (INamedFactory)this.container.GetInstance(facType);
            return factory.GetByKey(key);
        }
    }

    public object GetInstance(Type serviceType)
    {
        return this.container.GetInstance(serviceType);
    }

    object IServiceProvider.GetService(Type serviceType)
    {
        ((IServiceProvider)this.container).GetService(serviceType);
    }
}

Этот класс использует следующие два пользовательских интерфейса:

public interface INamedFactory
{
    object GetByKey(string key);
}

public interface INamedFactory<T> : INamedFactory
{
    T GetByKey(string key);
}

Используя этот пользовательский LightCoreServiceLocatorAdapter и эти два пользовательских интерфейса, вы можете создавать собственные фабрики, такие как этот универсальный:

public sealed class NamedDelegateFactory<T> : INamedFactory<T>
{
    private readonly Func<string, T> factory;

    public NamedDelegateFactory(Func<string, T> factory)
    {
        this.factory = factory;
    }

    public T GetByKey(string key)
    {
        return this.factory(key);
    }

    object INamedFactory.GetByKey(string key)
    {
        return this.factory(key);
    }
}

Который может быть зарегистрирован в контейнере следующим образом:

var factory = new NamedDelegateFactory<IMyType>(key =>
{
    if (key == "Cool") return new MyType1();
    else return new MyType2();
});

var builder = new ContainerBuilder();
builder.Register<INamedFactory<IMyType>>(c => factory);

Далее LightCoreServiceLocatorAdapter можно создать следующим образом:

var adapter = new LightCoreServiceLocatorAdapter(builder.Build());

Microsoft.Practices.ServiceLocation.ServiceLocator
    .SetLocatorProvider(() => adapter);

Вы можете зарегистрировать все именованные экземпляры с помощью интерфейса INamedFactory<T> и использовать NamedDelegateFactory<T>, обернутый делегатом, или реализовать пользовательский тип, который реализует INamedFactory<T>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...