首页 > 解决方案 > Autofac没有解析其他项目中的接口

问题描述

我正在尝试编写一个通用命令总线(类库的一部分),它在我的每个服务中使用不同的命令和处理程序。

以下代码产生以下异常:

System.Exception:命令没有任何处理程序 RegisterUserCommand

我的印象是传递我的 UserService 的 ExecutingAssemblies 将允许容器解析我的 UserService 中的处理程序,但显然不是。

难道我做错了什么?

命令总线:

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}");
        }
    }
}

容器构建器:

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;
    }

命令处理程序:

 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();
        }

谢谢,

标签: c#asp.netdependency-injectionautofac

解决方案


主要错误在这里:

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

RegisterUserComnmandHandler不是一个ICommandHandler而是一个ICommandHandler<RegisterUserCommand>。代替IsAssignableTo<>方法,您可以使用IsClosedTypeOfwhich 是Autofac扩展,它可以尽您所能。

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

顺便说一句,在您的代码示例中,您正在使用另一个容器。大多数时候,为整个应用程序拥有一个容器总是很简单的。要使事情井井有条,您可以使用autofac 模块。您还直接从容器中解析并且不使用范围,这意味着您的实例图不会在操作结束时被释放,而是会在容器的整个生命周期内保留。

在您的控制器中,我看到您正在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);
    };
}); 

它看起来你不需要它,对我来说似乎没用


推荐阅读