在ASP.NET MVC中实现存储库模式

时间:2011-03-03 19:02:00

标签: asp.net-mvc entity-framework-4 repository-pattern unit-of-work service-layer

我仍然很难绕过这个。我想像我这样分开我的图层(dll):

1)MyProject.Web.dll - MVC Web App(控制器,模型(编辑/视图),视图)
2)MyProject.Services.dll - 服务层(业务逻辑)
3)MyProject.Repositories.dll - 存储库
4)MyProject.Domain.dll - POCO类 5)MyProject.Data.dll - EF4

工作流:

1)控制器调用服务以获取对象以填充视图/编辑模型 2)服务调用存储库来获取/持久化对象 3)存储库调用EF以从SQL Server获取/持久化对象。

我的存储库返回IQueryable(Of T),在其中它们使用ObjectSet(Of T)。

所以我看到这一点,图层完全依赖于下一层和包含POCO类的lib?

一些担忧:

1)现在我的存储库与EF一起正常工作,它们将依赖于System.Data.Objects,现在我在我的存储库层与EF紧密耦合,是不是很糟糕?

2)我正在使用UnitOfWork模式。那住哪儿?它有一个Property Context As ObjectContext,因此它也与EF紧密耦合。坏?

3)我如何使用DI使这更容易?

我希望这是一个松散耦合的测试。有什么建议吗?

----------编辑----------

如果我在这里正确的轨道,请告诉我。另外,因此服务器注入一个IRepository(Of Category)权限,它如何知道它与EFRepository(Of T)的具体类之间的区别?与UnitOfWork和服务相同吗?

一旦有人帮助我把这个想法发现在我所理解的地方,我知道这看起来似乎微不足道,但男人,我有一段时间在我身边徘徊!!

控制器

Public Class CategoryController
    Private _Service As Domain.Interfaces.IService

    Public Sub New(ByVal Service As Domain.Interfaces.IService)
        _Service = Service

    End Sub

    Function ListCategories() As ActionResult
        Dim Model As New CategoryViewModel

        Using UOW As New Repositories.EFUnitOfWork
            Mapper.Map(Of Category, CategoryViewModel)(_Service.GetCategories)
        End Using

        Return View(Model)
    End Function

End Class

服务

Public Class CategoryService

    Private Repository As Domain.Interfaces.IRepository(Of Domain.Category)
    Private UnitOfWork As Domain.Interfaces.IUnitOfWork

    Public Sub New(ByVal UnitOfWork As Domain.Interfaces.IUnitOfWork, ByVal Repository As Domain.Interfaces.IRepository(Of Domain.Category))
        UnitOfWork = UnitOfWork
        Repository = Repository

    End Sub

    Public Function GetCategories() As IEnumerable(Of Domain.Category)
        Return Repository.GetAll()
    End Function

End Class

存储库和UnitOfWork

Public MustInherit Class RepositoryBase(Of T As Class)
    Implements Domain.Interfaces.IRepository(Of T)

End Class

Public Class EFRepository(Of T As Class)
    Inherits RepositoryBase(Of T)

End Class

Public Class EFUnitOfWork
    Implements Domain.Interfaces.IUnitOfWork

    Public Property Context As ObjectContext

    Public Sub Commit() Implements Domain.Interfaces.IUnitOfWork.Commit

    End Sub

End Class

4 个答案:

答案 0 :(得分:7)

原始答案

  1. 没有。但是,为避免将服务与此耦合,请在域层中设置ISomethingRepository接口。这将由您的IoC容器解决。

  2. 工作单元模式应与您的存储库一起实施。使用相同的解决方案来解决这个问题,就像我建议将存储库与服务分离一样。在您的域图层中创建IUnitOfWorkIUnitOfWork<TContext>,并将实现放在您的Repository图层中。如果所有存储库都将数据保存到数据层中的ObjectContext,我认为您的存储库实现不需要与数据层分离,我认为没有任何理由。 Repository接口是域逻辑,但实现是一个数据问题

  3. 您可以使用DI将服务注入控制器,将存储库注入服务。使用DI,您的服务将依赖于存储库接口ISomethingRepository,并将接收EFSomethingRepository的实现,而不会耦合到数据/存储库程序集。基本上,您的IControllerFactory实现将使IoC容器为Controller提供所有构造函数依赖项。这将要求IoC容器还为其构造函数依赖项(存储库)提供所有控制器的构造函数依赖项(服务)。所有程序集都依赖于您的域层(具有存储库和服务接口),但不会相互依赖,因为它们依赖于接口而不是实现。您将需要单独的程序集来进行依赖性解析,或者您需要在Web项目中包含该代码。 (我会推荐一个单独的组装)。唯一依赖于依赖关系解析程序集的程序集将是UI程序集,但如果使用IHttpModule实现在Application_Start事件中注册依赖项,则即使这不是完全必要的(项目将仍然需要bin文件夹中的dll副本,但不需要项目引用)。有很多合适的开源IoC容器。最好的一个很大程度上取决于你的选择。我个人喜欢StructureMap。它和Ninject都是可靠且记录良好的DI框架。

  4. 对Sam Striano的编辑的回应

    自从我用VB编写代码以来已经有好几年了,所以我的语法可能会关闭。

    Public Class CategoryController
      Private _Service As Domain.Interfaces.IService
    
      'This is good.
      Public Sub New(ByVal Service As Domain.Interfaces.IService)
          _Service = Service
      End Sub
    
    
      Function ListCategories() As ActionResult
          Dim Model As New CategoryViewModel
    
    
          Using UOW As New Repositories.EFUnitOfWork
    

    这不需要在控制器中。将其移动到存储库并使其包围实际的事务。此外,您不希望控制器依赖于数据层。

              Mapper.Map(Of Category, CategoryViewModel)(_Service.GetCategories)
    

    这是对AutoMapper的调用吗?与原始问题无关,但是,您应该将映射功能重定位到ActionFilter,以便返回只是返回视图(_Service.GetCategories)

          End Using
    
          Return View(Model)
      End Function
    

    服务类没有问题。

    存储库和工作单元看起来大部分都不完整。您的存储库应该新建ObjectContext并将其注入工作单元,然后执行工作单元范围内的所有事务(类似于您在控制器中执行的操作)。在Controller中使用它的问题是,可能将单个Service调用范围限定为多个工作单元。这是一篇关于如何实施工作单元的好文章。 http://martinfowler.com/eaaCatalog/unitOfWork.html。 Martin Fowler的书籍和网站是关于这些类型主题的重要信息来源。

答案 1 :(得分:1)

按顺序回答您的疑虑

1)不一定是坏的,取决于你坚持使用EF的可能性。你可以做几件事来减少这个。一个相对较低的成本(假设您有一些控制反转设置,如果没有跳到3)是仅从您的服务引用您的存储库的接口。

2)同样,我认为你可以花很多时间不让你的应用程序没有加入EF,但你必须问自己,这种方向的改变是否也不会导致其他变化。同样,可以通过接口引入一层间接,并在以后轻松地将一个存储库实现交换出来。

3)控制反转应该再次允许您想要的所有测试。因此,根本不需要许多直接引用,并且可以单独测试任何层。

请求样本的更新。

public class QuestionService : IQuestionService
{

    private readonly IQuestionRepository _questionRepository;

    public QuestionService(IQuestionRepository questionRepository){
           _questionRepository = questionRepository
    }
}

因此,您的服务只知道可以在单元测试中模拟或伪造的接口。这都是非常标准的IoC内容。在这方面有很多很好的参考,如果这对你来说很新,那么我会推荐一些book给你一个完整的故事。

答案 2 :(得分:0)

我建议使用MEF。它为您提供了所需的依赖注入框架,但它并不完整;它非常适合单元测试。以下是相关问题的几个答案:Simplifying Testing through design considerations while utilizing dependency injection

答案 3 :(得分:0)

使用MEF和Repository Pattern(也使用EFCodeFirst)可以找到here的完整代码示例。