也许我在这里和这里的问题还不够清楚,所以我将尝试减少冗长性,同时又不降低清晰度。
假设我的控制器使用DI(您可以“用脑袋说出它-不必大声说出来”);所以它看起来像这样:
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository is null");
}
_deptsRepository = deptsRepository;
}
这样,我可以拦截Web API路由机制,让它将实现IDepartmentRepository的特定类传递给Controller的构造函数。因此,如果我的界面是:
public interface IDepartmentRepository
{
int Get();
IEnumerable<Department> Get(int ID, int CountToFetch);
Department Add(Department item);
void Post(Department dept);
void PostDepartment(int accountid, string name);
void Put(Department dept);
void Delete(int Id);
}
...我可以有这样的课程:
public class DepartmentRepositoryProductionData : IDepartmentRepository
{
private readonly List<Department> departments = new List<Department>();
public DepartmentRepository()
{
using (var conn = new OleDbConnection(
@"Provider=Microsoft.ACE.OLEDB.12.0;User ID=BlaBlaBla..."))
{
...
...还有这个:
public class DepartmentRepositoryTestData : IDepartmentRepository
{
private readonly List<Department> departments = new List<Department>();
public DepartmentRepository()
{
// Load test data from an XML file (or text file, or whatever)
...
现在,这只是花花公子,但我如何/在何处指定我希望DepartmentRepositoryTestData(或DepartmentRepositoryProductionData)的实例成为Controller实例化的实例?我衷心地接受将类的标识延迟到运行时的概念,但是即使在阅读了很多有关DI的知识之后,我也缺少了有关如何以及在何处指定控制器将接收哪个类(实现所需接口)的部分。在其构造函数arg中。
它是由客户端,IOW通过URI中传递的内容指定的吗?我认为不会,但是我有点困惑,因此“一切皆有可能”。
由于这显然还不够清楚,因此我将添加以下内容:
为了使这种抽象(Controller构造函数的接口arg)具有任何值,必须有多个实现该接口的类,对吗?这可以是“测试数据”类和“生产数据”类,也可以是“加利福尼亚披萨”类和“纽约披萨”类,或者是全部实现的加利福尼亚,纽约,芝加哥和意大利类IPizzaPie。
但是某个地方的交通警察必须说:“实例化加州阶级”或“实例化纽约阶级”等。
这是我的问题:交通警察(类实例决策者)在哪里/在哪里,我们如何告诉它要通过哪个类?
似乎指定类类型的地方可能是在调用此构造函数(在我的“ WindsorCompositionRoot.cs”中)时:
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var controller =
(IHttpController)this.container.Resolve(controllerType);
request.RegisterForDispose(
new Release(
() => this.container.Release(controller)));
return controller;
}
...但是我仍然不知道谁叫这个(IHttpController)...
通常,如果您正在执行某种形式的TDD,并且想要编写隔离的测试,则建议不要连接容器来执行测试。您可能希望从单元测试中完全删除该容器。
因此,如果您想测试控制器,则可以执行以下操作:
var sut = new DepartmentsController(new DepartmentRepositoryTestData());
假设您确实希望为IDepartmentRepository拥有一个具体的测试类,而不是使用某种模拟框架。
如果这更多的是集成测试,并且您确实想连接一个容器以测试类之间的交互,那么这将是您的容器注册代码的责任。您应该对测试故事和生产故事分别进行容器注册,以便服务可以根据上下文解析为不同的具体实例。
编辑
我认为您实际上是在问两个问题:一个是关于路由和参数的问题,另一个是关于DI的问题。我将从DI的角度回答。
您说的是:
为了使这种抽象(Controller构造函数的接口arg)具有任何值,必须有多个实现该接口的类,对吗?
您是说接口/抽象仅在有多个实现的情况下有用吗?我认为这是一个见解,但是许多使用DI的人都会创建接口来促进松散耦合,即使只有一个(生产)实现也是如此。通常,第二个或后续实现是模拟的。
这可以是“测试数据”类和“生产数据”类,也可以是“加利福尼亚披萨”类和“纽约披萨”类,或者是全部实现的加利福尼亚,纽约,芝加哥和意大利类IPizzaPie。
但是某个地方的交通警察必须说:“实例化加州阶级”或“实例化纽约阶级”等。
这是我的问题:交通警察(类实例决策者)在哪里/在哪里,我们如何告诉它要通过哪个类?
我认为您在这里寻找的是所谓的“抽象工厂”。您可以注册一个接口的多个实例,然后创建一个工厂,该工厂根据某些条件选择实现。
关于这个主题的好文章:http : //blog.ploeh.dk/2012/03/15/ImplementinganAbstractFactory/
编辑#2
您在上次更新中发布的代码负责为ASP.Net MVC解决正确的控制器。这仅仅是MVC使用DI进行工作所必需的管道。
您正在尝试根据某种上下文返回服务的特定实现。如上文所述,通常使用抽象工厂来完成。您将创建一个工厂接口,并通过构造函数将其注入到控制器中,然后在控制器方法或构造函数中这样调用它:
IDepartmentRepository存储库= _departmentRepoFactory.Create(myContext);
从任何角度看,某些地方的代码都必须请求容器提供基于某种东西(字符串,枚举,整数等)的实现,而这正是抽象工厂的所在。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句