在C#控制台应用程序中正确使用Autofac
我是新使用Autofac的,所以我为noob的问题道歉。 我阅读了互联网上的每一本手册,解释了使用Autofac(或任何其他工具,如Structuremap、Unity等)的基本原理。但我发现的所有例子都是基础。我需要知道如何在我的代码中更深入地实现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
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调用到应用程序本身调用的“传递”应用程序
类,它是完全注入的,并且可以进行单元测试。这是真正的代码应该存在的地方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());
}
}