在C#控制台应用程序中正确使用Autofac

Seb*_*cia 8 c# dependency-injection inversion-of-control autofac

我是新用的Autofac,所以我为noob问题道歉.我阅读了互联网上的每本手册,解释了使用Autofac(或任何其他工具,如Structuremap,Unity等)时的基本知识.但我发现的所有例子都是基础知识.我需要知道如何在我的代码中更深入地实现Autofac.让我试着解释一下我需要知道的这个例子,一个控制台应用程序.

class Program
{
    static void Main(string[] args)
    {
        var container = BuildContainer();
        var employeeService = container.Resolve<EmployeeService>();
        Employee employee = new Employee
        {
            EmployeeId = 1,
            FirstName = "Peter",
            LastName = "Parker",
            Designation = "Photographer"
        };

        employeeService.Print(employee);
    }

    static IContainer BuildContainer()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<EmployeeRepository>().As<IEmployeeRepository>();
        builder.RegisterType<EmployeeService>();
        return builder.Build();
    }
}
Run Code Online (Sandbox Code Playgroud)

这很简单.我想弄清楚的是当你深入研究代码时如何实现这一点.在此示例中,执行此行时

employeeService.Print(employee);
Run Code Online (Sandbox Code Playgroud)

让我们假设"Print"方法有点复杂,需要使用其他依赖项/类来完成他的任务.我们仍在使用Autofac,所以我想我们需要像上面的例子那样创建依赖项.那是对的吗?在我的"print"方法中,当我需要使用另一个类时,我必须创建另一个容器,填充它,与Resolve()一起使用它等等?有一种更简单的方法吗?在所有解决方案中都可以使用具有所需所有依赖关系的静态类?怎么样?我希望能够清楚.也许我无法表达我的需要.:(抱歉我的英语很差.我在学习Autofac时仍在学习它.

Joh*_* Wu 27

静态就是问题所在

控制台程序的主要问题是主Program类主要是静态的.这不适合单元测试,对IoC也不好; 例如,永远不会构造静态类,因此没有构造函数注入的机会.结果,您最终new在主代码库中使用,或从IoC容器中提取实例,这违反了模式(此时更多的是服务定位器模式).我们可以通过回到将代码放在实例方法中的实践来摆脱这种混乱,这意味着我们需要一个对象实例.但是什么东西?

一个两级模式

在编写控制台应用程序时,我遵循特定的轻量级模式.欢迎您遵循这种模式,这对我来说非常有用.

该模式涉及两个类:

  1. 原始Program类,它是静态的,非常简短,并且从代码覆盖范围中排除.此类充当从O/S调用到适当调用应用程序的"传递".
  2. 一个实例Application类,完全注入并可单元测试.这是您的真实代码应该存在的地方.

计划类

O/S需要一个Main入口点,它必须是静态的.该Program班的存在只是为了满足这一要求.

保持静态程序非常干净; 它应该包含(1)组合根和(2)一个简单的"传递"入口点,它调用真实的应用程序(正如我们将看到的那样是实例化的).

所有代码Program都不值得进行单元测试,因为它所做的只是组成对象图(无论如何在测试中都会有所不同)并调用应用程序的主入口点.通过隔离非单元可测试代码,您现在可以从代码覆盖中排除整个类(使用ExcludeFromCodeCoverageAttribute).

这是一个例子:

[ExcludeFromCodeCoverage]
static class Program
{
    private static IContainer CompositionRoot()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<Application>();
        builder.RegisterType<EmployeeService>().As<IEmployeeService>();
        builder.RegisterType<PrintService>().As<IPrintService>();
        return builder.Build();
    }

    public static void Main()  //Main entry point
    {
        CompositionRoot().Resolve<Application>().Run();
    }
}
Run Code Online (Sandbox Code Playgroud)

如你所见,非常简单.

Application类

现在实现你的Application课程,好像它是独一无二的程序.只有现在,因为它是实例化的,你可以按照通常的模式注入依赖项.

class Application
{
    protected readonly IEmployeeService _employeeService;
    protected readonly IPrintService _printService;

    public Application(IEmployeeService employeeService, IPrintService printService)
    {
        _employeeService = employeeService; //Injected
        _printService = printService; //Injected
    }

    public void Run()
    {
        var employee = _employeeService.GetEmployee();
        _printService.Print(employee);
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法可以分离关注点,避免过多的静态"东西",让您无需太多麻烦就可以遵循IoC模式.你会注意到 - new除了实例化一个ContainerBuilder之外,我的代码示例不包含关键字的单个实例.

如果依赖项具有自己的依赖项怎么办?

因为我们遵循这种模式,如果PrintServiceEmployeeService拥有自己的依赖关系,容器现在将处理所有这些.只要您在组合根中的相应接口下注册它们,就不必实例化或编写任何代码来获取注入的服务.

class EmployeeService : IEmployeeService
{
    protected readonly IPrintService _printService;

    public EmployeeService(IPrintService printService)
    {
        _printService = printService; //injected
    }

    public void Print(Employee employee)
    {
        _printService.Print(employee.ToString());
    }
}
Run Code Online (Sandbox Code Playgroud)

这样容器可以处理所有事情,您不必编写任何代码,只需注册您的类型和接口即可.

  • 谢谢。这告诉我我必须如何做。再次感谢您的时间和出色的解释。 (2认同)