2012-05-16 140 views
33

我从网上看我这点,这表示接口用于此存储库模式 - 为什么我们需要接口?

  • 使用TDD方法
  • 更换持久性引擎

但我无法理解如何界面有用到这个点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(); 

请详细解释我,以及接口在存储库模式方面的其他有用性。

回答

69

那么接口如何进入画面?

像这样:

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做了一个例子:

  1. 安装Ninject.MVC3的NuGet
  2. 在生成~/App_Start/NinjectWebCommon.cs代码,你只需决定使用EF实现用一个单一的代码行:

    private static void RegisterServices(IKernel kernel) 
    { 
        kernel.Bind<IEmployeeRepository>().To<EmployeeRepositoryEF>(); 
    }   
    

这样,您不再需要做任何手工实例这些存储库类和担心upcasting或任何。它是为您管理它们的依赖注入框架,并负责将定义的实现注入到控制器构造函数中。

通过简单修改此配置,您可以切换数据访问技术,而无需触摸控制器中的单行代码。这样单元测试的方式也在发挥作用。由于您的控制器代码现在与存储库弱耦合(得益于我们介绍的接口),您需要在单元测试中做的所有事情是在存储库上提供一些模拟实现,以便您定义其行为。这使您可以单元测试索引控制器动作而不依赖于数据库或其他任何东西。完全隔离。

我还邀请您在ASP.NET MVC中检查关于TDD和DI的following articles

+1

奇妙的答案,每个解释都值得..现在我知道它是如何工作的..谢谢你,我不能将这个答案标记为接受,因为我的分数低于15,只要我赚取我会接受这个答案。 – Meson

+0

感谢那篇文章..并假设如果我配置'EmployeeRepositoryEF'在我的依赖注入框架中,我的控制器将消耗此'EmployeeRepositoryEF',但是如果我想要在同一控制器中使用2实现..如果这个问题愚蠢im非常抱歉.. – Meson

+2

在示例I提供程序中,控制器在其构造函数中需要一个'IEmployeeRepository'实例。只有一个实现可以通过。另一方面,你可能有另一个控制器可能需要不同的接口实现。这完全有可能。您只需配置您的DI框架,以便将ControllerA和ControllerB中的ImplementationB注入到ControllerA中。不同的DI框架之间的语法当然会有所不同。 –

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,或城堡温莎将履行这一职责)。

的一点是,消费代码不关心执行,只有合同(接口)。这使您可以非常容易地为了测试目的而更换实现,并促进松散耦合。

只是为了澄清一下,接口的使用和存储库模式之间没有任何联系,它只是另一种可以利用它们的模式。

+0

感谢您的快速响应......还有一个问题为什么向上转换为什么向上转换'IEmployee emp = new EmployeeRepository();'vs'EmployeeRepository emp = new EmployeeRepository();'? – Meson

相关问题