C# 停止Autofac模块注册已注册的组件

C# 停止Autofac模块注册已注册的组件,c#,autofac,C#,Autofac,我有一个Autofac模块,在负载覆盖中具有以下(精简)逻辑: protected override void Load(ContainerBuilder builder) { foreach (var componentType in allTypesInAllAvailableAssemblies) // Set elsewhere { var handlerInterfaces = componentType.GetInter

我有一个Autofac模块,在负载覆盖中具有以下(精简)逻辑:

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);
        }
    }
protected override void Load(ContainerBuilder生成器)
{
foreach(allTypesInAllAvailableAssemblies中的var componentType)//在别处设置
{
var handlerInterfaces=componentType.GetInterfaces(),其中(i=>i.IsClosedTypeOf(typeof(IMessageHandler));
if(handlerInterfaces.Any())
builder.RegisterType(componentType).As(handlerInterfaces);
}
}
这将查找任何声明自己为消息处理程序并针对其实现的所有IMessageHandler接口注册它的类

我想做的是,如果组件已经注册,就不要注册它。作为奖励,如果我可以更新现有的注册来解决消息处理程序接口(如果还没有)的问题,这将是一个理想的选择

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

在过去,我使用了
AttachToComponentRegistration
覆盖来进行注册操作,但在这种情况下它似乎没有什么用处


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

不幸的是,没有一种优雅的方式来实现您想要的。Autofac容器及其构建器是“黑匣子”,不能让您很好地查看已有的内容

两次注册一个组件并没有什么害处,除非您的注册依赖于顺序(坏的,坏的,坏的)。第二次注册只会用新注册覆盖旧注册

我严重质疑这段代码,因为它完全取决于AllTypesInAllAvailableAssembly是如何初始化的。如果它真的是你的系统中的每一种类型,那么它是一个垃圾射击什么将解决,比如说,一个IDisposable。如果您有多个不同的实现,比如IConfigurator,那么无论您是在检查已注册的内容,还是只是让注册被覆盖,您都将对最终注册的内容拥有有限的控制权;这完全取决于列表中的第一个(或最后一个)类

我唯一想做的就是使用一点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);
    }
}
protected override void Load(ContainerBuilder生成器)
{
foreach(allTypesInAllAvailableAssemblies.OfType().Distinct()中的var componentType)//在别处设置
{
var handlerInterfaces=componentType.GetInterfaces(),其中(i=>i.IsClosedTypeOf(typeof(IMessageHandler));
if(handlerInterfaces.Any())
builder.RegisterType(componentType).As(handlerInterfaces);
}
}
这将保证在这个foreach循环的范围内,构建器以前从未见过componentType的每个实例。这意味着,鉴于这是用于构建容器的唯一模块,并且每个容器仅构建一次且从未更新,系统中的每个组件将在任何给定容器中准确注册一次。公共接口,如IDisposable、IEnumerable、IComparable、IComparer等,都将毫无价值地尝试解决;它们将解析为具有该接口的最后一个类的实例

如果您必须验证接口从未注册过,或者在使用ContainerBuilder更新()现有容器时此代码也有效,只需停止您正在执行的操作,因为您将要创建一个无法正常维护的无望的混乱

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

将起作用。

之所以出现这种情况,是因为我为插件提供了双重调度注册机制(即,为插件提供了一个类,该类公开注册方法,并在内部将它们添加到构建器中)。这很好,很干净,可以用来注册处理程序,但我一直在寻找一种方法,不强迫插件显式地单独注册它们的所有处理程序(通过在shell中使用这个模块)。这也可以正常工作,除非组件在分派中注册,但同时也是消息处理程序(特别是在生命周期不同的情况下)。系统中存在多个接口没有问题(事实上,这是预期的)。也没有更新现有容器或注册的订单依赖性。我可能没有尽可能好地解释这个场景,但它并没有你想象的那么糟糕。哦,所有类型的所有可用程序集都只存在于SO示例中。在现实世界中,可用的类型是由另一个具有某些智能的类提供的。您是否尝试过使用AnyConcreteTypeNotAlreadyRegisteredSource类?请参阅:不幸的是,我确实需要处理程序列表。最后,我更改了调度注册,以便在显式注册类型时查找所有处理程序接口,而不是再次扫描所有类型。为帮助干杯——我已经接受了,因为它回答了张贴的问题。@Nichloas您的免责声明不再正确。PED()与
IEnumerable
配合使用效果很好。我想Autofac已经注意到了这个缺点,并找到了一个解决办法。谢谢你提醒我,@Brondahl!编辑。