C# 存储库模式-为什么我们需要接口?

C# 存储库模式-为什么我们需要接口?,c#,asp.net-mvc-3,repository-pattern,C#,Asp.net Mvc 3,Repository Pattern,我从互联网上读到,我得到了这一点,说接口是用来做这件事的 使用TDD方法 更换持久性引擎 但我无法理解界面在这一点上是如何发挥作用的替换持久性引擎。 让我们考虑一下,为 CaseRealPosiPosie < /P>创建一个基本的(没有泛型)存储库。 public class EmployeeRepository { public employee[] GetAll() { //here I'll return from dbContext or ObjectContex

我从互联网上读到,我得到了这一点,说接口是用来做这件事的

  • 使用TDD方法
  • 更换持久性引擎
但我无法理解界面在这一点上是如何发挥作用的
替换持久性引擎
。 让我们考虑一下,为<代码> CaseRealPosiPosie<代码> < /P>创建一个基本的(没有泛型)存储库。
public class EmployeeRepository
{
  public employee[] GetAll()
  {
     //here I'll return from dbContext or ObjectContex class
  }
}
那么界面是如何出现的呢

如果假设我创建了一个界面,为什么要使用向上转换呢?例如

 IEmployee emp = new EmployeeRepository() ;
 vs
 EmployeeRepository emp = new EmployeeRepository();

请向我详细解释界面在存储库模式方面的其他有用之处。

您将把存储库公开为一个界面:

public interface IEmployeeRepository
{
    List<Employee> GetAll();
}
IEmployeeRepository myRepo = MyRepositoryFactory.Get<IEmployeeRepository>();
公共接口IEmployeeRepository
{
List GetAll();
}
这将允许您拥有许多不同的接口实现,例如默认实现:

public class EmployeeRepository : IEmployeeRepository
{
    public List<Employee> GetAll()
    {
        // Return from db.
    }
}
公共类EmployeeRepository:IEEmployeeRepository
{
公共列表GetAll()
{
//从数据库返回。
}
}
或者一个测试:

public class TestEmployeeRepository : IEmployeeRepository
{
    public List<Employee> GetAll()
    {
        // Stub some dummy data.
    }
}
公共类TestEmployeeRepository:IEEmployeeRepository
{
公共列表GetAll()
{
//存根一些虚拟数据。
}
}
使用存储库的代码只对使用接口感兴趣:

public interface IEmployeeRepository
{
    List<Employee> GetAll();
}
IEmployeeRepository myRepo = MyRepositoryFactory.Get<IEmployeeRepository>();
IEmployeeRepository myRepo=MyRepositoryFactory.Get();
秘密酱汁是工厂,或者是将接口解析为可用类型的另一种机制(依赖项注入框架,如Ninject或Castle Windsor将履行此角色)

关键是,消费代码不关心实现,只关心契约(接口)。这使您可以非常轻松地交换用于测试目的的实现,并促进松耦合

只是澄清一下,接口的使用和存储库模式之间没有联系,具体来说,这只是另一种可以利用它们的模式

那么界面是如何出现的呢

像这样:

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执行此操作的示例:

  • 安装NuGet
  • 在生成的
    ~/App_Start/NinjectWebCommon.cs
    代码中,您只需决定将EF实现与一行代码一起使用:

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IEmployeeRepository>().To<EmployeeRepositoryEF>();
    }        
    
    私有静态无效注册服务(IKernel内核)
    {
    kernel.Bind().To();
    }        
    
  • 这样,您就不再需要对这些存储库类进行任何手动实例化,也不用担心升级或其他问题。依赖注入框架为您管理它们,并负责将定义的实现注入控制器构造函数

    只需修改此配置,您就可以切换数据访问技术,而无需触碰控制器中的一行代码。这就是独立单元测试发挥作用的方式。由于控制器代码现在与存储库弱耦合(多亏了我们介绍的接口),所以单元测试中需要做的就是在存储库上提供一些模拟实现,从而允许您定义其行为。这使您能够在不依赖数据库或其他任何东西的情况下对索引控制器操作进行单元测试。完全隔离


    我还邀请您在ASP.NET MVC中查看关于TDD和DI的信息。

    感谢您的快速回复。。。还有一个问题,为什么升级
    ieemployeeemp=newemployeerepository()vs
    雇员安置emp=新雇员安置()?精彩的答案,每一个解释都是值得的。现在我明白了事情是如何运作的。谢谢,我不能将这个答案标记为已接受,因为我的分数低于15分,一旦我得到答案,我将接受这个答案。谢谢那篇文章。。假设我在依赖注入框架中配置了
    EmployeeRepositoryEF
    ,我的控制器将使用这个
    EmployeeRepositoryEF
    ,但是如果我想在同一个控制器中使用2个实现呢。。如果这个问题很愚蠢,我很抱歉..在示例I提供程序中,控制器在其构造函数中需要一个
    IEmployeeRepository
    实例。只能通过一个实现。另一方面,您可能有另一个控制器,它可能需要不同的接口实现。这是完全可能的。您只需要配置DI框架,以便它在ControllerA中注入实现A,在ControllerB中注入实现B。当然,不同的DI框架的语法会有所不同。谢谢你,现在它对我来说很有意义,而且非常清晰。我完全同意这个概念,但假设我的项目中有1000个存储库。我认为如果我创建1000个接口,代码会有味道。应该是保持DI的一种方式,但避免有这么多接口。