Autofac, как зарегистрировать и разрешить универсальный интерфейс во время выполнения? - PullRequest
0 голосов
/ 27 сентября 2019

У меня проблемы с разрешением реализации универсального интерфейса во время выполнения.Я работаю с шиной событий, которая разрешает обработчики событий в зависимости от типа события.Когда я пытаюсь разрешить обработчики событий без универсальной реализации, все работает как положено.Я хочу реализовать универсальный интерфейс, чтобы у меня был базовый класс, который обрабатывает события определенного типа.

У меня была следующая ситуация перед созданием универсальной реализации:

public interface IEvent
    {
        Guid EntityId { get; set; }
    }

public interface IEventHandler<TEvent> where TEvent : IEvent
    {
        Task Handle(TEvent @event);
    }

public class EventBus
    {
        private readonly IComponentContext _context;

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

        public async Task HandleEvent<TEvent>(TEvent @event) where TEvent : IEvent
        {
            var handler = _context.Resolve<IEventHandler<TEvent>>();
            await handler.Handle(@event);
        }
    }

Я регистрируюсьeventHandlers следующим образом:

builder.RegisterAssemblyTypes(ThisAssembly).AsClosedTypesOf(typeof(IEventHandler<>));

Пример реализации:

public class FooEventHandler :
        IEventHandler<FooArchivedEvent>,
        IEventHandler<FooRestoredEvent>,
        IEventHandler<FooSomethingElseHappenedEvent>
    {
        private readonly IRepository<Foo> _repository;

        public FooEventHandler(IRepository<Foo> repository)
        {
            _repository = repository;
        }

        public async Task Handle(FooArchivedEvent @event)
        {
            var Foo = await _repository.Get(@event.EntityId);
            Foo.Archive();
        }

        public async Task Handle(FooRestoredEvent @event)
        {
            var Foo = await _repository.Get(@event.EntityId);
            Foo.Restore();
        }

        public async Task Handle(FooSomethingElseHappenedEvent @event)
        {
            // do something else with Foo
        }
    }

    public class BarEventHandler :
        IEventHandler<BarArchivedEvent>,
        IEventHandler<BarRestoredEvent>
    {
        private readonly IRepository<Bar> _repository;

        public BarEventHandler(IRepository<Bar> repository)
        {
            _repository = repository;
        }

        public async Task Handle(BarArchivedEvent @event)
        {
            var Bar = await _repository.Get(@event.EntityId);
            Bar.Archive();
        }

        public async Task Handle(BarRestoredEvent @event)
        {
            var Bar = await _repository.Get(@event.EntityId);
            Bar.Restore();
        }
    }

Когда я передаю FooArchivedEvent в шину событий, шина событий разрешает требуемый обработчик событий.Как видите, у меня есть некоторый дублирующий код, который я хочу разрешить в базовом обработчике событий.Это то, что я пробовал, прежде чем создать универсальную реализацию (которая не будет компилироваться):

public class BaseEventHandler<TEntity, TArchivedEvent, TRestoredEvent> :
        IEventHandler<TArchivedEvent>,
        IEventHandler<TRestoredEvent>
        where TEntity : class
        where TArchivedEvent : IEvent
        where TRestoredEvent : IEvent
    {
        public Task Handle(TArchivedEvent @event)
        {
            throw new NotImplementedException();
        }

        public Task Handle(TRestoredEvent @event)
        {
            throw new NotImplementedException();
        }
    }

Итак, я создал базовый базовый класс, который выполняет компиляцию, однако я не могу понять, как разрешитьуниверсальный обработчик событий в eventbus.Общий базовый класс:

public abstract class BaseEventHandler<TEntity> :
        IEventHandler<IArchivedEvent<TEntity>>,
        IEventHandler<IRestoredEvent<TEntity>>
        where TEntity : Archivable
    {
        protected readonly IRepository<TEntity> _repository;

        public BaseEventHandler(IRepository<TEntity> repository)
        {
            _repository = repository;
        }

        public virtual async Task Handle(IArchivedEvent<TEntity> @event)
        {
            var entity = await _repository.Get(@event.EntityId);
            entity.Archive();
        }

        public async virtual Task Handle(IRestoredEvent<TEntity> @event)
        {
            var entity = await _repository.Get(@event.EntityId);
            entity.Archive();
        }
    }

Мой новый FooEventHandler теперь будет выглядеть следующим образом:

public class FooEventHandler : BaseEventHandler<Foo>,
        IEventHandler<FooSomethingElseHappenedEvent>
    {
        public FooEventHandler(IRepository<Foo> repository) : base(repository)
        {
        }

        public Task Handle(FooSomethingElseHappenedEvent @event)
        {
            // do something else with Foo
        }
    }

Теперь, когда я передаю FooArchivedEvent в шину событий, шина событий не может разрешить обработчик событий.Есть ли что-то, что мне нужно сделать в части регистрации, или невозможно разрешить общую реализацию, подобную этой?

1 Ответ

0 голосов
/ 30 сентября 2019

В итоге я написал метод, который проверяет каждый интерфейс типа на наличие зарегистрированного обработчика:

private IEventHandler<TEvent> GetHandler<TEvent>(Type type = null) where TEvent : IEvent
        {
            object handler;
            type = type ?? typeof(TEvent);
            if (_container.TryResolve(typeof(IEventHandler<>).MakeGenericType(type), out handler))
            {
                return (IEventHandler<TEvent>)handler;
            }
            else
            {
                foreach (var t in type.GetInterfaces())
                {
                    var h = GetHandler<TEvent>(t);
                    if (h != null)
                        return h;
                }
            }
            return null;
        }
...