那么接口是如何出现的呢?
像这样:
public interface IEmployeeRepository
{
Employee[] GetAll();
}
然后你可以有尽可能多的实现:
public class EmployeeRepositoryEF: IEmployeeRepository
{
public Employee[] GetAll()
{
//here you will return employees after querying your EF DbContext
}
}
public class EmployeeRepositoryXML: IEmployeeRepository
{
public Employee[] GetAll()
{
//here you will return employees after querying an XML file
}
}
public class EmployeeRepositoryWCF: IEmployeeRepository
{
public Employee[] GetAll()
{
//here you will return employees after querying some remote WCF service
}
}
and so on ... you could have as many implementation as you like
如您所见,我们如何实现存储库并不重要。重要的是所有存储库和实现都尊重定义的合同(接口),并且都拥有一个GetAll
返回员工列表的方法。
然后您将拥有一个使用此接口的控制器。
public class EmployeesController: Controller
{
private readonly IEmployeeRepository _repository;
public EmployeesController(IEmployeeRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
var employees = _repository.GetAll();
return View(employees);
}
}
看看控制器如何不再依赖于存储库的特定实现?它只需要知道这个实现尊重合同。现在你需要做的就是配置你喜欢的依赖注入框架来使用你想要的实现。
这是如何使用 Ninject 完成此操作的示例:
- 安装Ninject.MVC3 NuGet
在生成的~/App_Start/NinjectWebCommon.cs
代码中,您只需决定将 EF 实现与一行代码一起使用:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IEmployeeRepository>().To<EmployeeRepositoryEF>();
}
这样,您不再需要对这些存储库类进行任何手动实例化,也无需担心向上转换或其他任何事情。它是为您管理它们的依赖注入框架,并将负责将定义的实现注入到控制器构造函数中。
只需修改此配置,您就可以切换数据访问技术,而无需触及控制器中的任何一行代码。这就是孤立的单元测试也发挥作用的方式。由于您的控制器代码现在与存储库弱耦合(感谢我们引入的接口),您在单元测试中需要做的就是在存储库上提供一些模拟实现,以便您定义其行为。这使您可以对索引控制器操作进行单元测试,而不依赖于数据库或其他任何东西。完全隔离。
我还邀请您查看以下有关 ASP.NET MVC 中的 TDD 和 DI 的文章。