使用 Autofac 动态解决依赖关系
Resolving dependencies dynamically using Autofac
像我这样动态地解决依赖关系好吗?到处都建议使用Constructor注入。我真的不明白按照我的方式做这件事的缺点。代码片段如下..
Employee.cs
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Department Department { get; set; }
}
IRepository.cs
public interface IRepository<TModel> where TModel : class
{
void Add();
IEnumerable<TModel> GetAll();
IEnumerable<TModel> GetByID();
}
Repository.cs
public class Repository<TModel> : IRepository<TModel> where TModel : class
{
public void Add()
{
throw new NotImplementedException();
}
public IEnumerable<TModel> GetAll()
{
throw new NotImplementedException();
}
public IEnumerable<TModel> GetByID()
{
throw new NotImplementedException();
}
}
EmployeeController.cs
public class HomeController : ApiController
{
IComponentContext _container;
public HomeController(IComponentContext container)
{
this._container = container;
}
public Repository<TModel> Using<TModel>() where TModel :class
{
var repository = _container.Resolve(typeof(IRepository<TModel>));
return repository as Repository<TModel>;
}
[HttpGet]
public IEnumerable<Employee> GetEmployees()
{
return Using<Employee>().GetAll();
}
}
Global.asax
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
var container = builder.Build(Autofac.Builder.ContainerBuildOptions.None);
var webApiResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = webApiResolver;
}
假设我有 5 个存储库,构造函数注入将解决我发出的请求的所有 5 个依赖项。我可能不会为每个请求都使用 5 个存储库。所以我想通过传递类型来动态解决依赖关系,就像我在 Using<TModel>()
中所做的那样。任何建议,将不胜感激..!!谢谢...!!
避免在应用程序组件中直接使用容器;这会导致各种麻烦,例如可维护性和可测试性问题。直接从应用程序代码中解析实例是一个 well-known anti-pattern,称为 Service Locator。
作为第一次重构,您可以改为应用 Unit of Work 模式。工作单元允许访问底层存储库。例如:
public interface IUnitOfWork
{
IRepository<TModel> Repository<TModel>();
}
public sealed class HomeController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
public HomeController(IUnitOfWork unitOfWork)
{
this._unitOfWork = unitOfWork;
}
[HttpGet]
public IEnumerable<Employee> GetEmployees()
{
return this._unitOfWork.Repository<Employee>().GetAll();
}
}
在 Composition Root (where it is allowed 中访问容器),我们现在可以创建一个 IUnitOfWork
动态解析存储库的实现:
private sealed class AutofacUnitOfWork : IUnitOfWork
{
private readonly IComponentContext _container;
public AutofacUnitOfWork(IComponentContext container)
{
this._container = container;
}
public IRepository<TModel> Repository<TModel>()
{
return _container.Resolve<IRepository<TModel>>();
}
}
此模式大大简化了您的应用程序组件并防止了服务定位器反模式通常导致的缺点。
虽然应用工作单元模式可能是朝着正确方向迈出的有用一步,但更好的方法是直接跳过工作单元,直接将所需的存储库直接注入应用程序组件:
public sealed class HomeController : ApiController
{
private readonly IRepository<Employee> _employeeRepository;
public HomeController(IRepository<Employee> employeeRepository)
{
this._employeeRepository = employeeRepository;
}
[HttpGet]
public IEnumerable<Employee> GetEmployees()
{
return this._employeeRepository.GetAll();
}
}
Say i've 5 repositories, Constructor injection will resolve all the 5 dependencies for a request i make. I might not use 5 repositories for each and every request.
请注意,从性能的角度来看,您通常不应该关心是否使用依赖项。 Autofac 在大多数情况下足够快,这实际上不太可能导致您的生产系统出现任何性能问题。
然而,从设计的角度来看,如果 class 有很多依赖关系,而方法只使用其中的几个,你应该更担心。这意味着 class 中的方法几乎没有内聚性。这表明 class 应该分成多个更小的 class;它有 multiple responsibilities.
像我这样动态地解决依赖关系好吗?到处都建议使用Constructor注入。我真的不明白按照我的方式做这件事的缺点。代码片段如下..
Employee.cs
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Department Department { get; set; }
}
IRepository.cs
public interface IRepository<TModel> where TModel : class
{
void Add();
IEnumerable<TModel> GetAll();
IEnumerable<TModel> GetByID();
}
Repository.cs
public class Repository<TModel> : IRepository<TModel> where TModel : class
{
public void Add()
{
throw new NotImplementedException();
}
public IEnumerable<TModel> GetAll()
{
throw new NotImplementedException();
}
public IEnumerable<TModel> GetByID()
{
throw new NotImplementedException();
}
}
EmployeeController.cs
public class HomeController : ApiController
{
IComponentContext _container;
public HomeController(IComponentContext container)
{
this._container = container;
}
public Repository<TModel> Using<TModel>() where TModel :class
{
var repository = _container.Resolve(typeof(IRepository<TModel>));
return repository as Repository<TModel>;
}
[HttpGet]
public IEnumerable<Employee> GetEmployees()
{
return Using<Employee>().GetAll();
}
}
Global.asax
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
var container = builder.Build(Autofac.Builder.ContainerBuildOptions.None);
var webApiResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = webApiResolver;
}
假设我有 5 个存储库,构造函数注入将解决我发出的请求的所有 5 个依赖项。我可能不会为每个请求都使用 5 个存储库。所以我想通过传递类型来动态解决依赖关系,就像我在 Using<TModel>()
中所做的那样。任何建议,将不胜感激..!!谢谢...!!
避免在应用程序组件中直接使用容器;这会导致各种麻烦,例如可维护性和可测试性问题。直接从应用程序代码中解析实例是一个 well-known anti-pattern,称为 Service Locator。
作为第一次重构,您可以改为应用 Unit of Work 模式。工作单元允许访问底层存储库。例如:
public interface IUnitOfWork
{
IRepository<TModel> Repository<TModel>();
}
public sealed class HomeController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
public HomeController(IUnitOfWork unitOfWork)
{
this._unitOfWork = unitOfWork;
}
[HttpGet]
public IEnumerable<Employee> GetEmployees()
{
return this._unitOfWork.Repository<Employee>().GetAll();
}
}
在 Composition Root (where it is allowed 中访问容器),我们现在可以创建一个 IUnitOfWork
动态解析存储库的实现:
private sealed class AutofacUnitOfWork : IUnitOfWork
{
private readonly IComponentContext _container;
public AutofacUnitOfWork(IComponentContext container)
{
this._container = container;
}
public IRepository<TModel> Repository<TModel>()
{
return _container.Resolve<IRepository<TModel>>();
}
}
此模式大大简化了您的应用程序组件并防止了服务定位器反模式通常导致的缺点。
虽然应用工作单元模式可能是朝着正确方向迈出的有用一步,但更好的方法是直接跳过工作单元,直接将所需的存储库直接注入应用程序组件:
public sealed class HomeController : ApiController
{
private readonly IRepository<Employee> _employeeRepository;
public HomeController(IRepository<Employee> employeeRepository)
{
this._employeeRepository = employeeRepository;
}
[HttpGet]
public IEnumerable<Employee> GetEmployees()
{
return this._employeeRepository.GetAll();
}
}
Say i've 5 repositories, Constructor injection will resolve all the 5 dependencies for a request i make. I might not use 5 repositories for each and every request.
请注意,从性能的角度来看,您通常不应该关心是否使用依赖项。 Autofac 在大多数情况下足够快,这实际上不太可能导致您的生产系统出现任何性能问题。
然而,从设计的角度来看,如果 class 有很多依赖关系,而方法只使用其中的几个,你应该更担心。这意味着 class 中的方法几乎没有内聚性。这表明 class 应该分成多个更小的 class;它有 multiple responsibilities.