0

私はこのクラスを持っています:

public class AutofacEventContainer : IEventContainer
    {
        private readonly IComponentContext _context;

        public AutofacEventContainer(IComponentContext context)
        {
            _context = context;
        }

        public IEnumerable<IDomainEventHandler<T>> Handlers<T>(T domainEvent)
                                               where T : IDomainEvent
        {
            return _context.Resolve<IEnumerable<IDomainEventHandler<T>>>();
        }
    }

このIEventContainerような外観:

public interface IEventContainer
    {

        IEnumerable<IDomainEventHandler<T>> Handlers<T>(T domainEvent)
            where T : IDomainEvent;
    }

これは、次のようなIEventContainerクラスで使用されます。DomainEvents

public static class DomainEvents
    {

        ....................................
        ....................................

        public static IEventContainer Container;




        public static void Raise<T>(T domainEvent) where T : IDomainEvent
        {
            if (Container != null)
                foreach (var handler in Container.Handlers(domainEvent))
                    handler.Handle(domainEvent);

            // registered actions, typically used for unit tests.
            if (_actions != null)
                foreach (var action in _actions)
                    if (action is Action<T>)
                        ((Action<T>)action)(domainEvent);
        }


    }

私の目的はDomainEvents.Container、すべてのハンドラーが解決されるように登録することです。

 public class SomeModule : Module
   {
    protected override void Load(ContainerBuilder builder)
        {
            base.Load(builder);

            //Wrong way in Autofac
            //Because the following line is Ninject-like
            DomainEvents.Container = new AutofacContainer(componentContext);

           //What is the correct way to register it to achieve the above intent?

         }
    }

Autofacでこれを行う方法は何ですか?

4

1 に答える 1

1

ルート スコープ外で解決すると、メモリ リークが発生します。( Autofac のライフタイム スコープの概要については、この投稿を参照してください。)それを行うためのより良い方法は、次のようになります。

interface IEventRaiser
{
    void Raise<TEvent>(TEvent @event) where TEvent : IDomainEvent;
}

class AutofacEventRaiser : IEventRaiser
{
    private readonly ILifetimeScope context;

    public AutofaceEventRaiser(ILifetimeScope context)
    {
        this.context = context;
    }

    public void Raise<TEvent>(TEvent @event) where TEvent : IDomainEvent
    {
        using(var scope = context.BeginLifetimeScope("eventRaiser"))
        {
            foreach(var handler in scope.Resolve<IEnumerable<IDomainEventHandler<TEvent>>>())
            {
                handler.Handle(@event);
            }
        } // scope is disposed - no memory leak
    }
}


// then, in the composition root:

IContainer theContainer = BuildTheAutofacContainer();
DomainEvents.EventRaiser = new AutofacEventRaiser(theContainer);

これは簡単な答えですが、注意すべき点がもう 1 つあります...

問題は、本当に static を使用するかどうかですIEventRaiser。DI の純粋主義者は一般に、「いいえ、インスタンスをIEventRaiser必要とするすべてのクラスに自分のインスタンスを挿入する必要があります」と言うでしょうが、静的イベント レイザーは問題ないと主張する人もいます。

コンポーネントの共有に影響を与える可能性があるため、この決定を行う前に、Autofac の有効期間スコープがどのように機能するかを確認してください。たとえばSomeClass、 のインスタンスがSomeDependencyあり、 が発生するとしますSomeEventSomeDependencyまた、 であると仮定しましょうInstancePerLifetimeScope

  • SomeClassが注入された場合IEventRaiser、ハンドラーは同じ有効期間スコープで呼び出され、 の同じインスタンスで注入されますSomeDependency
  • SomeClassstatic を使用する場合IEventRaiser、ハンドラーは別の有効期間スコープで呼び出され、 の別のインスタンスで注入されますSomeDependency

SomeDependencyこれが DB トランザクションのようなものであると想像すると、これが重要な理由がわかります。

于 2014-01-31T16:31:43.353 に答える