停止Autofac模块注册已经注册的组件

本文关键字:注册 组件 模块 停止 Autofac | 更新日期: 2023-09-27 18:16:56

我有一个Autofac模块,它在Load override中具有以下(修剪)逻辑:

protected override void Load(ContainerBuilder builder)
{
    foreach (var componentType in allTypesInAllAvailableAssemblies) // Set elsewhere
    {
        var handlerInterfaces = componentType.GetInterfaces().Where(i => i.IsClosedTypeOf(typeof(IMessageHandler<>)));
        if (handlerInterfaces.Any())                    
            builder.RegisterType(componentType).As(handlerInterfaces);
    }
}

这是寻找任何类声明自己是一个消息处理程序,并注册它实现的所有IMessageHandler接口。

我想做的是而不是注册组件,如果它已经注册了。作为额外的好处,如果我可以更新现有的注册来解析消息处理程序接口(如果还没有的话),那将是理想的。

为了便于讨论,可以假设此代码将在所有其他类型(包括可能的消息处理程序候选)注册之后运行

我过去使用过AttachToComponentRegistration覆盖来进行注册操作,但它看起来在这种情况下并不有用。

这是可能的还是我应该重新考虑我的设计和强制插件显式声明他们的处理程序?

停止Autofac模块注册已经注册的组件

builder.RegisterType(componentType)
    .As(handlerInterfaces)
    .PreserveExistingDefaults();

不幸的是,没有一种优雅的方式来完成您想要的。Autofac容器和它的构建器都是"黑盒",不能让你很好地查看你已经拥有的东西。

注册一个组件两次没有害处,除非你的注册是顺序依赖的(BAD, BAD, BAD)。第二次注册只会用新的注册覆盖旧的注册。

我严重质疑这段代码,因为它完全取决于allTypesInAllAvailableAssemblies是如何初始化的。如果您的系统中的每种类型都需要它,那么对于哪种类型的解决方案(例如,IDisposable)来说,这是一种废话。如果你有几个不同的实现,比如说,iconfigator,你将有有限的控制哪一个最终注册,不管你是检查什么已经注册或只是让注册被覆盖;这完全取决于列表中的第一个(或最后一个)类。

我唯一能想到的就是使用一点Linq来确保你注册的类型列表是唯一的:

protected override void Load(ContainerBuilder builder)
{
    foreach (var componentType in allTypesInAllAvailableAssemblies.OfType<Type>().Distinct()) // Set elsewhere
    {
        var handlerInterfaces = componentType.GetInterfaces().Where(i => i.IsClosedTypeOf(typeof(IMessageHandler<>)));
        if (handlerInterfaces.Any())
            builder.RegisterType(componentType).As(handlerInterfaces);
    }
}

这将保证在foreach循环的作用域中,componentType的每个实例以前从未被构建器看到过。这意味着,假设这是用于构建容器的唯一模块,并且每个容器只构建一次且从不更新,那么系统中的每个组件将在任何给定的容器中精确地注册一次。常见的接口,如IDisposable, IEnumerable, IComparable, IComparer等,都是没有价值的;它们将解析为具有该接口的最后一个类的实例。

如果你必须验证一个接口从未被注册过,或者当使用ContainerBuilder来更新()一个现有的容器时,这段代码也可以工作,请停止你正在做的事情,因为你将创建一个无望的混乱,你将永远无法正确维护