Получите доступ или получите Autofac Container внутри статического класса

Мне нужно получить или получить доступ к моему контейнеру IoC в статическом классе. Это мой (упрощенный) сценарий:

Я регистрирую зависимости для ASP .net Web Api в классе Startup (но я также делаю это для MVC или WCF. У меня есть проект DependecyResolver, но для простоты рассмотрим следующий код)

// Web Api project - Startup.cs
public void Configuration(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();

    var builder = new ContainerBuilder();

    // ... Omited for clarity
    builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
        .AsClosedTypesOf(typeof(IHandle<>))
        .AsImplementedInterfaces();

    // ...
    IContainer container = builder.Build();
    config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    // ...
}

Затем в отдельной библиотеке классов у меня есть статический класс (снова упрощенный для ясности):

public static class DomainEvents
{
    private static IContainer Container { get; set; }

    static DomainEvents()
    {
        //Container = I need get my Autofac container here
    }

    public static void Register<T>(Action<T> callback) where T : IDomainEvent { /* ... */ }

    public static void ClearCallbacks() { /* ... */  }

    public static void Raise<T>(T args) where T : IDomainEvent
    {
        foreach (var handler in Container.Resolve<IEnumerable<IHandle<T>>>())
        {
            handler.Handle(args);
        }
        // ...
    }
}

Любая идея, как я могу получить это?

2 ответа

Решение

Мне нужно получить или получить доступ к моему контейнеру IoC в статическом классе. Любая идея, как я могу получить это?

Да нет! Шутки в сторону. Узор со статикой DomainEvents класс происходит от Udi Dahan, но даже Udi признал, что это был плохой дизайн. Статические классы, которые требуют собственных зависимостей, чрезвычайно болезненны для работы. Они затрудняют тестирование и обслуживание системы.

Вместо этого создайте IDomainEvents абстрагирование и внедрение реализации этой абстракции в классы, которые требуют публикации событий. Это полностью решит вашу проблему.

Вы можете определить свой DomainEvents Класс следующим образом:

public interface IDomainEvents
{
    void Raise<T>(T args) where T : IDomainEvent;
}

// NOTE: DomainEvents depends on Autofac and should therefore be placed INSIDE
// your Composition Root.
private class AutofacDomainEvents : IDomainEvents
{
    private readonly IComponentContext context;
    public AutofacDomainEvents(IComponentContext context) {
        if (context == null) throw new ArgumentNullException("context");
        this.context = context;
    }

    public void Raise<T>(T args) where T : IDomainEvent {
        var handlers = this.context.Resolve<IEnumerable<IHandle<T>>>();
        foreach (var handler in handlers) {
            handler.Handle(args);
        }
    }
}

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

IContainer container = null;

var builder = new ContainerBuilder();

builder.RegisterType<AutofacDomainEvents>().As<IDomainEvent>()
    .InstancePerLifetimeScope();

// Other registrations here

container = builder.Build();

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

public static class DomainEvents
{
    public static void SetContainer(IContainer container)
    {
        Container = container;
    }

    ....
}

А затем из вашего приложения ASP.NET вызовите этот метод для внедрения контейнера:

DomainEvents.SetContainer(container);

Обратите внимание, что я даю вам прямой ответ на ваш вопрос. Тем не менее, вот некоторые проблемы, которые я вижу с этим:

  • Статические классы не должны использоваться, когда класс требует зависимостей. В таком случае рефакторинг должен использовать нестатический класс и использовать Constructor Injection для внедрения зависимостей, которые вам нужны в классе.
  • Использование контейнера за пределами корня композиции называется местоположением службы и считается анти-шаблоном.
  • Библиотеки классов не должны использовать контейнер или даже иметь корень композиции. Цитирую из статьи Composition Root, на которую я ссылался:

Только приложения должны иметь корни композиции. Библиотеки и фреймворки не должны.

Другие вопросы по тегам