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

Mes*_*son 39 c# repository-pattern asp.net-mvc-3

我从互联网上读到了我得到了这一点,其中说接口用于此

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

但是我无法理解接口在这一点上的用途Replace persistance engine.让我们考虑我正在创建一个基本的(没有泛型)存储库EmployeeRepository

public class EmployeeRepository
{
  public employee[] GetAll()
  {
     //here I'll return from dbContext or ObjectContex class
  }
}
Run Code Online (Sandbox Code Playgroud)

界面界面如何形成?

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

 IEmployee emp = new EmployeeRepository() ;
 vs
 EmployeeRepository emp = new EmployeeRepository();
Run Code Online (Sandbox Code Playgroud)

请详细解释我,以及关于存储库模式的接口的其他有用性.

Dar*_*rov 78

界面界面如何形成?

像这样:

public interface IEmployeeRepository
{
    Employee[] GetAll();
}
Run Code Online (Sandbox Code Playgroud)

然后你可以拥有任意数量的实现:

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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我们如何实现存储库并不重要.重要的是,所有存储库和实现都遵循已定义的合同(接口),并且所有存储库都拥有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);
    }   
}
Run Code Online (Sandbox Code Playgroud)

了解控制器如何不再依赖于存储库的特定实现?它需要知道的是,这种实现尊重合同.现在,您需要做的就是配置您喜欢的依赖注入框架以使用您希望的实现.

以下是使用Ninject完成此操作的示例:

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

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IEmployeeRepository>().To<EmployeeRepositoryEF>();
    }        
    
    Run Code Online (Sandbox Code Playgroud)

这样,您不再需要对这些存储库类进行任何手动实例化,并担心向上转换或其他任何问题.它是依赖注入框架,它为您管理它们,并将负责将定义的实现注入到控制器构造函数中.

通过简单地修改此配置,您可以切换数据访问技术,而无需触及控制器中的单行代码.这种方式单独进行单元测试也会发挥作用.由于您的控制器代码现在与存储库弱耦合(感谢我们引入的接口),您在单元测试中需要做的就是在存储库上提供一些模拟实现,允许您定义其行为.这使您可以对Index控制器操作进行单元测试,而不依赖于数据库或其他任何操作.完全隔离.

我还邀请您查看ASP.NET MVC中有关TDD和DI 的以下文章.

  • 精彩的答案,每个解释都是值得的..现在我明白了事情是如何运作的..谢谢,我无法将此答案标记为已接受,因为我的分数低于 15,一旦我赚到,我就会接受这个答案。 (2认同)
  • 在示例I提供程序中,控制器期望在其构造函数中使用"IEmployeeRepository"实例.只能传递一个实现.另一方面,您可能有另一个控制器可能需要不同的接口实现.这完全有可能.您只需配置DI框架,以便它在ControllerA中的ControllerA和ImplementationB中注入ImplementationA.语法当然会因不同的DI框架而异. (2认同)

Ada*_*rth 14

您将把您的存储库公开为一个接口:

public interface IEmployeeRepository
{
    List<Employee> GetAll();
}
Run Code Online (Sandbox Code Playgroud)

这将允许您有许多不同的接口实现,例如默认的:

public class EmployeeRepository : IEmployeeRepository
{
    public List<Employee> GetAll()
    {
        // Return from db.
    }
}
Run Code Online (Sandbox Code Playgroud)

或测试一:

public class TestEmployeeRepository : IEmployeeRepository
{
    public List<Employee> GetAll()
    {
        // Stub some dummy data.
    }
}
Run Code Online (Sandbox Code Playgroud)

您使用存储库的代码只对使用该接口感兴趣:

IEmployeeRepository myRepo = MyRepositoryFactory.Get<IEmployeeRepository>();
Run Code Online (Sandbox Code Playgroud)

秘诀是工厂,或者将接口解析为可用类型的其他机制(诸如Ninject之类的依赖注入框架,或者Castle Windsor将履行此角色).

关键是,消费代码不关心实现,只关注合同(接口).这使您可以非常轻松地更换实现以进行测试,并促进松散耦合.

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