我已经从互联网上读到了我得到了这一点,其中说明了接口用于此
但我无法理解界面如何对这一点Replace persistance engine
有用。
我们可以考虑为EmployeeRepository
public class EmployeeRepository
{
public employee[] GetAll()
{
//here I'll return from dbContext or ObjectContex class
}
}
那么界面如何形成?
如果假设我创建了一个界面,为什么要使用upcasting?例如
IEmployee emp = new EmployeeRepository() ;
vs
EmployeeRepository emp = new EmployeeRepository();
请准确地解释我,以及关于存储库模式的接口的其他有用性。
答案 0 :(得分:77)
那么界面如何形成?
像这样:
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完成此操作的示例:
在生成的~/App_Start/NinjectWebCommon.cs
代码中,您只需使用一行代码来决定使用EF实现:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IEmployeeRepository>().To<EmployeeRepositoryEF>();
}
这样,您不再需要对这些存储库类进行任何手动实例化,并担心向上转换或其他任何问题。依赖注入框架为您管理它们,并将把定义的实现注入到控制器构造函数中。
通过简单地修改此配置,您可以切换数据访问技术,而无需触及控制器中的单行代码。这种方式单独进行单元测试也会发挥作用。由于您的控制器代码现在与存储库弱耦合(感谢我们引入的接口),您在单元测试中需要做的就是在存储库上提供一些模拟实现,允许您定义其行为。这使您可以对索引控制器操作进行单元测试,而不依赖于数据库或其他任何操作。完全隔离。
我还邀请您查看有关ASP.NET MVC中TDD和DI的following articles。
答案 1 :(得分:14)
您将把您的存储库公开为接口:
public interface IEmployeeRepository
{
List<Employee> GetAll();
}
这将允许您拥有许多不同的实现接口,例如默认接口:
public class EmployeeRepository : IEmployeeRepository
{
public List<Employee> GetAll()
{
// Return from db.
}
}
或测试一:
public class TestEmployeeRepository : IEmployeeRepository
{
public List<Employee> GetAll()
{
// Stub some dummy data.
}
}
您使用存储库的代码只对使用该接口感兴趣:
IEmployeeRepository myRepo = MyRepositoryFactory.Get<IEmployeeRepository>();
秘诀就是工厂,或者将界面解析为可用类型的其他机制(Ninject等依赖注入框架,或者Castle Windsor将履行此角色)。
重点是,消费代码并不关心实施,只有 合同 (接口)。这使您可以非常轻松地交换实现以进行测试,并促进松散耦合。
只是为了澄清,接口的使用和存储库模式之间没有任何关联,它只是另一种可以利用它们的模式。