背景
使用Autofac进行依赖注入时,经常遇到的场景是在容器中进行类似如下代码的注入操作:
builder.RegisterType<BackInStockSubscriptionService>().As<IBackInStockSubscriptionService>().InstancePerLifetimeScope(); builder.RegisterType<CategoryService>().As<ICategoryService>().InstancePerLifetimeScope(); builder.RegisterType<CompareProductsService>().As<ICompareProductsService>().InstancePerLifetimeScope(); builder.RegisterType<RecentlyViewedProductsService>().As<IRecentlyViewedProductsService>().InstancePerLifetimeScope(); builder.RegisterType<ManufacturerService>().As<IManufacturerService>().InstancePerLifetimeScope(); builder.RegisterType<PriceFormatter>().As<IPriceFormatter>().InstancePerLifetimeScope(); builder.RegisterType<ProductAttributeFormatter>().As<IProductAttributeFormatter>().InstancePerLifetimeScope(); builder.RegisterType<ProductAttributeParser>().As<IProductAttributeParser>().InstancePerLifetimeScope();
因为这种注入是统一进行的,随着项目规模的扩大和代码复杂度的增加,需要在注册的地方不断地增加新的类来注册类型,这样会使代码失去了开闭原则:对修改关闭,对扩展开放。
解决方案
1. 定义一个属性用于标识类的注入意图,在应用初始化时利用反射进行集中类型注册。
public class InjectableAttribute : Attribute { }
2. 在需要注入的类型中,使用[Injectable]标记注入
public class Employee { public string Name { get; set; } public string Password { get; set; } } public interface IEmployeeManager { IList<Employee> GetAllEmployess(); } public interface IEmployeeRepository { IList<Employee> GetAllEmployess(); } [Injectable] public class EmployeeRepository : IEmployeeRepository { public IList<Employee> GetAllEmployess() { List<Employee> list = new List<Employee>(); list.Add(new Employee { Name = "a", Password = "b" }); return list; } } [Injectable] public class EmployeeManager : IEmployeeManager { private readonly IEmployeeRepository _employeeRepository; public EmployeeManager(IEmployeeRepository employeeRepository) { _employeeRepository = employeeRepository; } public IList<Employee> GetAllEmployess() { var employeeData = _employeeRepository.GetAllEmployess(); var employeeList = new List<Employee>(); foreach (var employee in employeeData) { var employ = new Employee(); employ.Password= employee.Password; employ.Name = employee.Name; employeeList.Add(employ); } return employeeList; } }
3. 在WebApi的Controller中进行注入操作
public class EmployeeController : ApiController { private readonly IEmployeeManager _employeeManager; public EmployeeController(IEmployeeManager employeeManager) { _employeeManager = employeeManager; } [AllowAnonymous] [ResponseType(typeof(Employee))] public IHttpActionResult GetEmployees() { var employeeData = _employeeManager.GetAllEmployess(); return Ok(employeeData); } }
4. 项目中引用 Autofac.WebApi2 and AutoFac,定义如下容器初始化类
public static class AutofacDependecyBuilder { public static void DependencyBuilder() { // Create the builder with which components/services are registered. var builder = new ContainerBuilder(); // Register your Web API controllers. builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies()) .Where(t => t.GetCustomAttribute<InjectableAttribute>() != null) .AsImplementedInterfaces() .InstancePerRequest(); builder.RegisterWebApiFilterProvider(GlobalConfiguration.Configuration); //Build the Container var container = builder.Build(); //Create the Dependency Resolver AutofacWebApiDependencyResolver resolver = new AutofacWebApiDependencyResolver(container); //Configuring WebApi with Dependency Resolver GlobalConfiguration.Configuration.DependencyResolver = resolver; } }
5. 在Web程序开始处调用容器注册
public class WebApiApplication : System.Web.HttpApplication { protected void Application_Start() { GlobalConfiguration.Configure(WebApiConfig.Register); RouteConfig.RegisterRoutes(RouteTable.Routes); AutofacDependecyBuilder.DependencyBuilder(); } }
6.测试效果:
请求:
结果: