7

我仍然很难解决这个问题。我想像这样分离我的层(dll):

1) MyProject.Web.dll - MVC Web 应用程序(控制器、模型(编辑/视图)、视图)
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 类的库?

几个问题:

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

2)我正在使用 UnitOfWork 模式。那应该住在哪里?它有一个作为 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

存储库和工作单元

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

4 回答 4

7

原始答案

  1. 不可以。但是,为了避免将服务与此耦合,ISomethingRepository请在您的域层中有一个接口。这将由您的 IoC 容器解决。

  2. 工作单元模式应该与您的存储库一起实现。使用与我建议的将存储库与服务解耦相同的解决方案来解耦。在您的领域层中创建一个IUnitOfWorkIUnitOfWork<TContext>,并将实现放在您的存储库层中。如果所有存储库所做的只是将数据持久化到数据层中,我看不出您的存储库实现需要与数据层分开的任何理由ObjectContextRepository 接口是领域逻辑,但实现是数据关注点

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

对 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 以便您的返回只是 Return View(_Service.GetCategories)

      End Using

      Return View(Model)
  End Function

服务类没有问题。

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

于 2011-03-03T19:11:00.367 回答
1

按顺序回答您的疑虑

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

2) 同样,我认为您可能会花费大量时间不使您的应用程序不与 EF 耦合,但您必须问自己,这种方向的改变是否也不会对其他改变产生影响。同样,可以通过接口引入一个间接层,并在以后轻松地将一个存储库实现替换为另一个。

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

更新请求的样品。

public class QuestionService : IQuestionService
{

    private readonly IQuestionRepository _questionRepository;

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

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

于 2011-03-03T19:09:33.890 回答
0

我建议使用 MEF。它为您提供了您想要的依赖注入框架,但它并不成熟;它非常适合单元测试。以下是相关问题的一些答案:通过设计考虑简化测试,同时利用依赖注入

于 2011-03-03T19:08:20.100 回答
0

完整的代码示例可以在这里找到MEF 和存储库模式(也使用 EFCodeFirst)。

于 2011-03-14T21:01:33.583 回答