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

在C#控制台应用程序中正确使用Autofac,c#,dependency-injection,inversion-of-control,autofac,C#,Dependency Injection,Inversion Of Control,Autofac,我是新使用Autofac的,所以我为noob的问题道歉。 我阅读了互联网上的每一本手册,解释了使用Autofac(或任何其他工具,如Structuremap、Unity等)的基本原理。但我发现的所有例子都是基础。我需要知道如何在我的代码中更深入地实现Autofac。让我试着用这个例子来解释我需要知道什么,一个控制台应用程序 class Program { static void Main(string[] args) { var container = Build

我是新使用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();
    }
}
假设“Print”方法有点复杂,需要使用其他依赖项/类来完成任务。我们仍然在使用Autofac,所以我想我们需要像上面的例子那样创建依赖项。对吗?在我的“print”方法中,当我需要使用另一个类时,我必须创建另一个容器,填充它,使用Resolve()等等?有更简单的方法吗?一个具有所需所有依赖项的静态类可以在所有解决方案中使用?怎么用? 我希望澄清。也许我都无法表达我的需求(
对不起,我的英语很差。我在学习Autofac的同时还在学习它。

我们的想法是,您在启动时注册所有依赖项,然后您可以稍后解决它们。您看起来就快到了,只是做了一些更改:

class Program
{
    // Declare your container as a static variable so it can be referenced later
    static IContainer Container { get; set; }

    static void Main(string[] args)
    {
        // Assign the container to the static IContainer
        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();
    }
}

这是对代码的轻微修改(以适合您的代码)。您可以通过构造函数使用注入依赖项(Autofac还支持属性和方法注入)

通常,当完成依赖项注册时,不应该在类内使用容器,因为它会使类与容器耦合,在某些情况下,您可能希望使用子容器(内部作用域),您可以在其中定义一个特定的类来实现这一点,并使您的代码独立于容器

在您的示例中,您只需要解析IEmployeeService,它的所有依赖项都将由容器自动解析

下面是一个示例,演示如何实现这一点:

using Autofac;
using System;
using System.Collections.Generic;
using System.Linq;

namespace AutofacExample
{
    public class Employee
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public interface IEmployeeRepository
    {
        Employee FindById(int id);
    }

    public interface IEmployeeService
    {
        void Print(int employeeId);
    }

    public class EmployeeRepository : IEmployeeRepository
    {
        private readonly List<Employee> _data = new List<Employee>()
        {
            new Employee { Id = 1, Name = "Employee 1"},
            new Employee { Id = 2, Name = "Employee 2"},
        };
        public Employee FindById(int id)
        {
            return _data.SingleOrDefault(e => e.Id == id);
        }
    }

    public class EmployeeService : IEmployeeService
    {
        private readonly IEmployeeRepository _repository;
        public EmployeeService(IEmployeeRepository repository)
        {
            _repository = repository;
        }
        public void Print(int employeeId)
        {
            var employee = _repository.FindById(employeeId);
            if (employee != null)
            {
                Console.WriteLine($"Id:{employee.Id}, Name:{employee.Name}");
            }
            else
            {
                Console.WriteLine($"Employee with Id:{employeeId} not found.");
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var container = BuildContainer();
            var employeeSerive = container.Resolve<IEmployeeService>();
            employeeSerive.Print(1);
            employeeSerive.Print(2);
            employeeSerive.Print(3);
            Console.ReadLine();
        }

        static IContainer BuildContainer()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<EmployeeRepository>()
                   .As<IEmployeeRepository>()
                   .InstancePerDependency();
            builder.RegisterType<EmployeeService>()
                   .As<IEmployeeService>()
                   .InstancePerDependency();
            return builder.Build();
        }
    }
}
使用Autofac;
使用制度;
使用System.Collections.Generic;
使用System.Linq;
名称空间AutoFaceExample
{
公营雇员
{
公共int Id{get;set;}
公共字符串名称{get;set;}
}
公共接口IEmployeeRepository
{
员工FindById(内部id);
}
公共接口IEmployeeService
{
作废打印(内部员工ID);
}
公共类EmployeeRepository:IEEmployeeRepository
{
私有只读列表_data=new List()
{
新员工{Id=1,Name=“Employee 1”},
新员工{Id=2,Name=“Employee 2”},
};
公共雇员FindById(内部id)
{
返回_data.SingleOrDefault(e=>e.Id==Id);
}
}
公共类EmployeeService:IEEmployeeService
{
私有只读IEEmployeeRepository存储库;
公共雇员服务(IEEmployeeRepository存储库)
{
_存储库=存储库;
}
公共作废打印(内部员工ID)
{
var employee=_repository.FindById(employeeId);
if(employee!=null)
{
WriteLine($“Id:{employee.Id},Name:{employee.Name}”);
}
其他的
{
WriteLine($“找不到Id为{employeeId}的员工”);
}
}
}
班级计划
{
静态void Main(字符串[]参数)
{
var container=BuildContainer();
var employeeSerive=container.Resolve();
员工服务打印(1);
员工服务。打印(2);
员工服务。打印(3);
Console.ReadLine();
}
静态IContainer BuildContainer()
{
var builder=new ContainerBuilder();
builder.RegisterType()
.As()
.InstancePerDependence();
builder.RegisterType()
.As()
.InstancePerDependence();
返回builder.Build();
}
}
}

假设您有
EmployeeService
类,它需要其他类才能打印:

public class EmployeeService 
{
    private readonly IEmployeeRepository _employeeRepository;
    private readonly IEmployeePrinter _printer;

    public EmployeeService(IEmployeeRepository employeeRepository, 
        IEmployeePrinter printer)
    {
        _employeeRepository = employeeRepository;
        _printer = printer;
    }
    public void PrintEmployee(Employee employee)
    {
        _printer.PrintEmployee(employee);
    }
}
然后是
IEmployeePrinter
的实现,它还有更多的依赖性:

public class EmployeePrinter : IEmployeePrinter
{
    private readonly IEmployeePrintFormatter _printFormatter;

    public EmployeePrinter(IEmployeePrintFormatter printFormatter)
    {
        _printFormatter = printFormatter;
    }

    public void PrintEmployee(Employee employee)
    {
        throw new NotImplementedException();
    }
}
您不需要更多的容器。您所要做的就是用一个容器注册每个类型,就像您所做的一样:

static IContainer BuildContainer()
{
    var builder = new ContainerBuilder();
    builder.RegisterType<EmployeeRepository>().As<IEmployeeRepository>();
    builder.RegisterType<EmployeePrinter>().As<IEmployeePrinter>();
    builder.RegisterType<SomeEmployeeFormatter>().As<IEmployeePrintFormatter>();
    builder.RegisterType<EmployeeService>();
    return builder.Build();
}
但是容器使您不必担心创建所有这些类,即使它们嵌套了很多层。

静态是问题所在 控制台程序的主要问题是主
程序
类大多是静态的。这不利于单元测试,也不利于IoC;例如,静态类从未构造过,因此没有构造函数注入的机会。因此,您最终在主代码库中使用
new
,或拉取实例它来自IoC容器,这违反了模式(在这一点上更像是一个错误)。我们可以通过将代码放在实例方法中的实践来摆脱这种混乱,这意味着我们需要某个对象的实例。但是什么呢

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

该模式包括两类:

  • 原始的
    程序
    类,它是静态的,非常简短,不包括在代码覆盖范围内。这个类充当从O/S调用到应用程序本身调用的“传递”
  • 一个实例化的
    应用程序
    类,它是完全注入的,并且可以进行单元测试。这是真正的代码应该存在的地方
  • 程序类 O/S需要一个
    Main
    入口点,并且必须是静态的。
    程序
    类的存在只是为了满足此要求

    保持静态程序非常稳定
    public class EmployeePrinter : IEmployeePrinter
    {
        private readonly IEmployeePrintFormatter _printFormatter;
    
        public EmployeePrinter(IEmployeePrintFormatter printFormatter)
        {
            _printFormatter = printFormatter;
        }
    
        public void PrintEmployee(Employee employee)
        {
            throw new NotImplementedException();
        }
    }
    
    static IContainer BuildContainer()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<EmployeeRepository>().As<IEmployeeRepository>();
        builder.RegisterType<EmployeePrinter>().As<IEmployeePrinter>();
        builder.RegisterType<SomeEmployeeFormatter>().As<IEmployeePrintFormatter>();
        builder.RegisterType<EmployeeService>();
        return builder.Build();
    }
    
    var service = new EmployeeService(
        new EmployeeRespository("connectionString"),
        new EmployeePrinter(new SomeEmployeeformatter()));
    
    [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();
        }
    }
    
    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);
        }
    }
    
    class EmployeeService : IEmployeeService
    {
        protected readonly IPrintService _printService;
    
        public EmployeeService(IPrintService printService)
        {
            _printService = printService; //injected
        }
    
        public void Print(Employee employee)
        {
            _printService.Print(employee.ToString());
        }
    }