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

时间:2012-05-16 09:54:45

标签: c# asp.net-mvc-3 repository-pattern

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

  • 使用TDD方法
  • 替换persistance引擎

但我无法理解界面如何对这一点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();

请准确地解释我,以及关于存储库模式的接口的其他有用性。

2 个答案:

答案 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完成此操作的示例:

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

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IEmployeeRepository>().To<EmployeeRepositoryEF>();
    }        
    
  3. 这样,您不再需要对这些存储库类进行任何手动实例化,并担心向上转换或其他任何问题。依赖注入框架为您管理它们,并将把定义的实现注入到控制器构造函数中。

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

    我还邀请您查看有关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将履行此角色)。

重点是,消费代码并不关心实施,只有 合同 (接口)。这使您可以非常轻松地交换实现以进行测试,并促进松散耦合。

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