在 C# 控制台应用程序中正确使用 Autofac [英] Correct use of Autofac in C# console application

查看:55
本文介绍了在 C# 控制台应用程序中正确使用 Autofac的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm new using Autofac so my apologies for the noob question. I read every manual in Internet explaining the basics when using Autofac (or any other tool like Structuremap, Unity, etc). But all the examples that I found are basics. I need to know how to implement Autofac deeper in my code. Let me try to explain what I need to know with this example, a console application.

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

这很简单.我想弄清楚的是,当您深入了解代码时,如何实现这一点.在这个例子中,当你执行这一行

This is simple and easy. What I'm trying to figure out is how do you implement this when you go deeper in the code. In this example, when you execute this line

employeeService.Print(employee);

让我们假设打印"方法有点复杂,需要使用另一个依赖项/类来完成他的任务.我们仍在使用 Autofac,所以我想我们需要像上面的例子那样做一些事情来创建依赖关系.那是对的吗?在我的打印"方法中,当我需要使用另一个类时,我必须创建另一个容器,填充它,将它与 Resolve() 一起使用等等?有没有更简单的方法来做到这一点?可以在所有解决方案中使用具有所有所需依赖项的静态类?如何?我希望清楚.也许我也无法表达我的需要.:(对不起,我的英语不好.我在学习 Autofac 的同时还在学习它.

Let's assume the "Print" method is a little bit complex and need to use another dependencies/classes to accomplish his task. We are still using Autofac so I suppose we need to do something like the example above to create that dependencies. Is that correct? Inside my "print" method, when I need to use another class, I must create another container, fill it, use it with Resolve() and so on? There is an easier way to do that? A static class with all the dependencies needed can be consumed across all the solution? How? I hope to be clear. Maybe neither I can express what I need. :( Sorry for my poor English. I'm still learning it while I learn Autofac.

推荐答案

静态是问题

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

Static is the problem

The main issue with a console program is that the main Program class is mostly static. This is not good for unit testing and it's not good for IoC; a static class is never constructed, for example, so there is no chance for constructor injection. As a result, you end up using new in the main code base, or pull instances from the IoC container, which is a violation of the pattern (it's more of a service locator pattern at that point). We can get out of this mess by returning to practice of putting our code in instance methods, which means we need an object instance of something. But what something?

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

I follow a particular, lightweight pattern when writing a console app. You're welcome to follow this pattern which works pretty well for me.

该模式涉及两个类:

  1. 原始的 Program 类,它是静态的,非常简短,并且被排除在代码覆盖范围之外.此类充当从 O/S 调用到应用程序调用的传递".
  2. 一个实例化的 Application 类,它是完全注入和单元可测试的.这是您真正的代码应该存在的地方.
  1. The original Program class, which is static, very brief, and excluded from code coverage. This class acts as a "pass through" from O/S invocation to invocation of the application proper.
  2. An instanced Application class, which is fully injected and unit-testable. This is where your real code should live.

程序类

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

The Program Class

The O/S requires a Main entry point, and it has to be static. The Program class exists only to meet this requirement.

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

Keep your static program very clean; it should contain (1) the composition root and (2) a simple, "pass-through" entry point that calls the real application (which is instanced, as we will see).

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

None of the code in Program is worthy of unit testing, since all it does is compose the object graph (which would be different when under test anyway) and call the main entry point for the application. And by sequestering the non-unit-testable code, you can now exclude the entire class from code coverage (using the 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();
    }
}

如您所见,非常简单.

现在实现您的 Application 类,就好像它是唯一的程序一样.只有现在,因为它是实例化的,您可以按照通常的模式注入依赖项.

Now to implement your Application class as if it were the One and Only Program. Only now, because it is instanced, you can inject dependencies per the usual pattern.

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

这种方法保持关注点分离,避免过多的静态东西",并让您无需过多打扰即可遵循 IoC 模式.您会注意到——我的代码示例不包含 new 关键字的单个实例,除了实例化 ContainerBuilder.

This approach keeps separation of concerns, avoids too much static "stuff," and lets you follow the IoC pattern without too much bother. And you'll notice-- my code example doesn't contain a single instance of the new keyword, except to instantiate a ContainerBuilder.

因为我们遵循这种模式,如果 PrintServiceEmployeeService 有自己的依赖项,容器现在会处理这一切.您不必实例化或编写任何代码来注入这些服务,只要您在组合根的适当接口下注册它们即可.

Because we follow this pattern, if PrintService or EmployeeService have their own dependencies, the container will now take care of it all. You don't have to instantiate or write any code to get those services injected, as long as you register them under the appropriate interface in the composition root.

class EmployeeService : IEmployeeService
{
    protected readonly IPrintService _printService;

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

    public void Print(Employee employee)
    {
        _printService.Print(employee.ToString());
    }
}

通过这种方式,容器会处理一切,您无需编写任何代码,只需注册您的类型和接口即可.

This way the container takes care of everything and you don't have to write any code, just register your types and interfaces.

这篇关于在 C# 控制台应用程序中正确使用 Autofac的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆