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

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

问题描述

我是使用Autofac的新手,因此我为菜鸟问题表示歉意. 我阅读了Internet上的每本手册,解释了使用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中的任何代码都不值得进行单元测试,因为它所做的只是组成对象图(无论如何,在测试中这都是不同的),并调用应用程序的主入口点.通过隔离不可测试的代码,您现在可以将整个类排除在代码范围之外(使用

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天全站免登陆