Autofac 模块应该注册自己的依赖模块吗?

Dav*_*nde 4 c# autofac autofac-module

考虑我有一个使用三个库 lib1、lib2 和 lib3 的应用程序。在每个库中,我都实现了一个Module注册在该库中实现的依赖项。

其中一些实现有自己的依赖项,例如,lib2 和 lib3 可能都需要 lib1 中存在的一些实现。

我的问题是,我是否让 lib2 和 lib3 中的模块在 lib1 中注册模块作为其Load实现的一部分?如果我的应用程序注册了 lib2 和 lib3 的模块,这是否会注册该模块两次?

或者我是否避免让一个模块注册另一个模块,将它留给应用程序,但缺点是启动时可能会丢失某些注册?

小智 5

我知道的有点晚了,但我认为回答这个问题可能会很好,而不仅仅是引用关于多重组合根源如何邪恶的经文......

如果我的应用程序注册了 lib2 和 lib3 的模块,这是否会注册该模块两次?

用 Autofac 4.8.1 测试,它确实:

public class Lib1Module: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Console.WriteLine("Registering Lib1Module");

        // Register Types...

    }
}

public class Lib2Module: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Console.WriteLine("Registering Lib2Module");

        builder.RegisterModule<Lib1Module>();

        // Register Types...

    }
}

public class Lib3Module: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Console.WriteLine("Registering Lib3Module");

        builder.RegisterModule<Lib1Module>();

        // Register Types...

    }
}

public class Program
{
    public void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule<Lib2Module>();
        builder.RegisterModule<Lib3Module>();

        using(var container = builder.Build())
        {
            // Do Stuff
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

public class Lib1Module: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Console.WriteLine("Registering Lib1Module");

        // Register Types...

    }
}

public class Lib2Module: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Console.WriteLine("Registering Lib2Module");

        builder.RegisterModule<Lib1Module>();

        // Register Types...

    }
}

public class Lib3Module: Module
{
    protected override void Load(ContainerBuilder builder)
    {
        Console.WriteLine("Registering Lib3Module");

        builder.RegisterModule<Lib1Module>();

        // Register Types...

    }
}

public class Program
{
    public void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterModule<Lib2Module>();
        builder.RegisterModule<Lib3Module>();

        using(var container = builder.Build())
        {
            // Do Stuff
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以在IComponentRegistry/ ContainerBuidlerModule基类内部Load使用来自IComponentRegistry-的属性创建它传递给的容器构建器)上的属性字典来解决此问题并强制模块的单一注册,如果Load方法Lib1Module更改为:

protected override void Load(ContainerBuilder builder)
{
    if (builder.Properties.ContainsKey(GetType().AssemblyQualifiedName))
    {
        return;
    }
    builder.Properties.Add(GetType().AssemblyQualifiedName, null);

    Console.WriteLine("Registering Lib1Module");

    // Register Types...

}
Run Code Online (Sandbox Code Playgroud)

然后输出变成:

Registering Lib2Module
Registering Lib1Module
Registering Lib3Module
Registering Lib1Module
Run Code Online (Sandbox Code Playgroud)

显然,如果 Lib2Module/Lib3Module 可以成为其他模块的依赖项,则必须将类似的代码放入它们的 Load 方法中,同样,如果任何模块使用AttachToRegistrationSource和/或 AttachToComponentRegistration希望确保它们只运行一次,它们也需要检查。或者(如果这是您需要做的很多事情,可能最好)您可以创建自己的类实现IModule并在Configure.

我确实在生产代码中使用了这种模式来减少重复量,因为我有多个入口点,它们有自己的组合根(例如,一个 web api、一个 web 应用程序和一个重复的控制台应用程序),但它们共享一个大块代码,我可以忍受这使我成为 DI 纯粹主义者中不受欢迎的角色。


Cyr*_*and 2

我不建议在您的图书馆内进行注册。在大多数情况下,您应该有一个组合根来组成您的所有应用程序。

组合根是应用程序中模块组合在一起的(最好)唯一位置。

这个概念在这里解释组成根

顺便说一句,如果您多次注册一个模块,Autofac将多次注册组件。如果您的库中必须有模块,您应该只创建注册库组件的模块。