Autofa c не разрешающие интерфейсы в другом проекте - PullRequest
1 голос
/ 09 марта 2020

Я пытаюсь написать обобщенную c командную шину (часть библиотеки классов), которая использует разные команды и обработчики в каждой из моих служб.

Следующий код выдает следующее исключение:

System.Exception: команда не имеет никакого обработчика RegisterUserCommand

У меня было впечатление, что передача ExecutingAssemblies моего UserService позволит контейнеру разрешить обработчик в моем UserService но, видимо, нет.

Я что-то не так делаю?

CommandBus:

public interface ICommandBus
{
    void Send<T>(T Command) where T : ICommand;
}
public class CommandBus : ICommandBus
{
    private IContainer Container { get; set; }

    public CommandBus(Assembly assembly)
    {
        Container = new CommandBusContainerConfig().Configure(assembly);
    }
    public void Send<TCommand>(TCommand command) where TCommand : ICommand
    {
        var handlers = Container.Resolve<IEnumerable<ICommandHandler<TCommand>>>().ToList();
        if (handlers.Count == 1)
        {
            handlers[0].Handle(command);
        }
        else if (handlers.Count == 0)
        {
            throw new System.Exception($"Command does not have any handler {command.GetType().Name}");
        }
        else
        {
            throw new System.Exception($"Too many registred handlers - {handlers.Count} for command {command.GetType().Name}");
        }
    }
}

ContainerBuilder:

public class CommandBusContainerConfig : IContainerConfig
{

    public IContainer Configure(Assembly executingAssembly)
    {
        var builder = new ContainerBuilder();

        builder.RegisterAssemblyTypes(executingAssembly)
            .Where(x => x.IsAssignableTo<ICommandHandler>())
            .AsImplementedInterfaces();

        builder.Register<Func<Type, ICommandHandler>>(c =>
        {
            var ctx = c.Resolve<IComponentContext>();

            return t =>
            {
                var handlerType = typeof(ICommandHandler<>).MakeGenericType(t);
                return (ICommandHandler)ctx.Resolve(handlerType);
            };
        });
        return builder.Build();
    }
}

В моем UserService (ASP. Net Core 3), это другой проект, который ссылается на вышеупомянутую CommandBus:

public class RegisterUserCommand : ICommand
{
    public readonly string Name;
    public readonly Address Address;
    public string MobileNumber;
    public string EmailAddress;

    public RegisterUserCommand(Guid messageId, string name, string mobileNumber, string emailAddress, Address address)
    {
        Name = name;
        Address = address;
        MobileNumber = mobileNumber;
        EmailAddress = emailAddress;
    }

CommandHandler:

 public class RegisterUserComnmandHandler : ICommandHandler<RegisterUserCommand>
{

    public void Handle(RegisterUserCommand command)
    {
        Console.WriteLine($"Create user {command.Name} {command.MobileNumber} - handler");
    }
}

Запуск:

public void ConfigureServices(IServiceCollection services)
    {

        services.AddSingleton<ICommandBus>(new CommandBus(Assembly.GetExecutingAssembly()));
    }

Контроллер:

    private readonly ICommandBus _commandBus;
    public UsersController(ICommandBus commandBus) {
        _commandBus = commandBus;
    }
    // POST api/values
    [HttpPost]
    public async Task<IActionResult> Post([FromBody]RegisterUserCommand command)
    {
            if (ModelState.IsValid)
            {
                CommandBus commandBus = new CommandBus(Assembly.GetExecutingAssembly());
                commandBus.Send(command);
                _commandBus.Send(Command); //Same result as above
                // return result
                return Ok(command);
            }
            return BadRequest();
        }

Спасибо,

Ответы [ 2 ]

1 голос
/ 09 марта 2020

Основная ошибка здесь:

builder.RegisterAssemblyTypes(executingAssembly)
       .Where(x => x.IsAssignableTo<ICommandHandler>())
       .AsImplementedInterfaces();

RegisterUserComnmandHandler - это не ICommandHandler, а ICommandHandler<RegisterUserCommand>. Вместо метода IsAssignableTo<> вы можете использовать расширение IsClosedTypeOf, которое является расширением Autofa c, которое делает именно то, что вы можете.

builder.RegisterAssemblyTypes(executingAssembly)
       .Where(x => x.IsClosedTypeOf(typeof(ICommandHandler<>)))
       .AsImplementedInterfaces();

Кстати, в вашем примере кода вы используете другой контейнер. В большинстве случаев всегда просто иметь один контейнер для всего приложения. Чтобы упорядочить вещи, вы можете использовать autofa c module . Вы также решаете прямо из контейнера и не используете scope , это означает, что ваш график экземпляра не будет расположен в конце операции, а останется на весь срок жизни контейнера.

В вашем контроллере я видел, что вы создаете новый CommandBus для каждого запроса, который создаст новый контейнер. Создание нового контейнера - сложная операция, и вам следует избегать ее частого выполнения, но только один раз при запуске приложения.

Также я не понимаю смысл этой регистрации:

builder.Register<Func<Type, ICommandHandler>>(c =>
{
    var ctx = c.Resolve<IComponentContext>();

    return t =>
    {
        var handlerType = typeof(ICommandHandler<>).MakeGenericType(t);
        return (ICommandHandler)ctx.Resolve(handlerType);
    };
}); 

Не похоже, что вам это нужно, и мне это кажется бесполезным

0 голосов
/ 09 марта 2020

Мне понадобилось время, чтобы понять. Но мой интерфейс CommandHandler был неправильно определен. Он должен выглядеть следующим образом:

public interface ICommandHandler { }
public interface ICommandHandler<T> : ICommandHandler where T : ICommand
{
    void Handle(T command);
}

}

При попытке разрешить CommandHandler в классе конфигурации Autofa c сбой .Where(x => x.IsAssignableTo<ICommandHandler>()) произошел из-за присвоения классу ICommandHandle<T> не ICommandHandler

...