Asp.net mvc 3 工作单元和存储库模式是打算一起使用还是两种解决方案?

Asp.net mvc 3 工作单元和存储库模式是打算一起使用还是两种解决方案?,asp.net-mvc-3,design-patterns,dependency-injection,Asp.net Mvc 3,Design Patterns,Dependency Injection,看完这个 这篇文章昨天发表了大约75次,我不清楚作者想说什么。他似乎指出UoW和Repository模式是用于解决类似问题的两种不同模式。换句话说,UoW更适合于在提交之前需要编排的多个对象,而Repository则用于更直接的持久性 当我进行更多的在线搜索以找到圣杯框架以允许我对应用程序进行单元测试时,我看到更多的文章似乎暗示UoW是存储库模式的一部分,反之亦然 哪种解释是正确的? 最后,我希望能够充分隔离我的应用程序,允许对内存中的对象模拟数据。您遇到的问题是人们误解了存储库模式 存储库的基

看完这个 这篇文章昨天发表了大约75次,我不清楚作者想说什么。他似乎指出UoW和Repository模式是用于解决类似问题的两种不同模式。换句话说,UoW更适合于在提交之前需要编排的多个对象,而Repository则用于更直接的持久性

当我进行更多的在线搜索以找到圣杯框架以允许我对应用程序进行单元测试时,我看到更多的文章似乎暗示UoW是存储库模式的一部分,反之亦然

哪种解释是正确的?
最后,我希望能够充分隔离我的应用程序,允许对内存中的对象模拟数据。

您遇到的问题是人们误解了存储库模式

存储库的基本思想是,您应该能够像对待一个大集合一样对待数据,而不必担心数据背后的内容。这意味着,如果向集合中添加对象,则不必担心该对象是否已保存。因此,存储库应始终在每个操作上保持不变


另一方面,UnitOfWork用于协调对数据库的多个调用,这些调用必须一起提交或回滚。标准银行账户转账就是一个例子。我不想一次只更改一个银行账户,因为如果我在保存第二个银行账户时遇到问题,那么就很难收回

就测试而言,两者都有效。这真的很简单(以最小起订量为例)

public void UpdateAddress()
{
//安排
User=//以某种方式构建您的用户。。
设置(x=>x.Get(It.IsAny())。返回(用户);
MyService sut=新的MyService(myUnitOfWorkMock);
//表演
sut.变更地址(用户ID,“铜斑蛇路1234号”);
//断言
Assert.AreEqual(“铜斑蛇路1234号”,用户地址);
}

请注意在该页面中遵循该建议。我做了,但效果不太好,我被烧伤了一点

您需要使用集成测试(涉及真实数据库的测试)而不是纯单元测试(使用内存中的数据存储)而不是单元测试来测试业务类。原因是你最终会得到错误通过和错误否定的单元测试

以下面的例子为例。假设您的业务逻辑想要查询截至今天已发出的任何请求。在业务逻辑中,我会写:

var requests = _unitOfWork.Requests.Where(x => x.RequestDate.Date = DateTime.Now.Today).ToList();
当针对内存中的对象运行时,这将正常工作,但这将触发
NotImplementedException
,因为EF无法有效处理此类查询。实体框架不支持Linq查询,但除非运行集成测试,否则无法捕获它们。因此,您的单元测试现在给您带来了误报,他们声称它在实际不起作用时起作用

现在假设您的业务逻辑想要查询特定用户发出的请求。最有可能的是,您的业务逻辑将以Linq语句结束,例如:

var requests = _unitofWork.Requests.Where(x => x.UserId == userid).ToList();
这个查询的问题是,为了使单元测试通过,您必须了解业务逻辑的实现。我的意思是,如果您对此进行了以下单元测试:

[TestMethod]
public void Can_Retrieve_User_Requests()
{
    // Setup
    var user = new User();
    var req1 = new Request();       
    user.Requests.Add(req1);

    _unitOfWork.Add(user);
    _unitOfWork.Add(req1);
    _unitOfWork.Commit();

    // Act
    var result = BusinessLogicClass.GetRequestsByUserId(user.Id);

    // Assert
    Assert.IsNotNull(result);
    Assert.AreEqual(1, result.count);
}
如果
\u unitOfWork
正在使用您的实体框架系统,则此单元测试可以正常工作并通过(当然所有其他方面都可以通过)

如果您使用的是内存中的工作单元,这将失败,原因有二。第一个是未设置
user.Id
,99%的时间Id将由实体框架生成。即使您明确地设置了
user.Id=X
,这仍然会失败,因为您还需要设置
request.UserId
。在EF中,创建新关系时将自动填充
request.UserId
字段,但在使用内存数据集时不会发生这种情况。这意味着您必须明确地知道
GetRequestsByUserId()
通过查看
Request.UserId
字段而不是
Request.User.Id
,来实现查询,这也意味着如果您将查询更改为使用
Request.User.Id
,那么在此实例中将单元测试更改为通过将失败,尽管EF中的业务逻辑和结果基本相同

最后一个示例是,如果数据库字段没有足够的空间来存储长字符串(EF CodeFirst默认db string LENGS为varchar(128))会发生什么情况。如果您有一个超过128个字符的测试,内存中的数据存储将很好地存储字符串,但EF将异常,因为它太长

TLDR:用于数据查询和数据存储的内存单元测试不会让您确信应用程序是否正常工作。您仍然需要创建集成测试,但是您的集成测试必须验证您的单元测试覆盖的99%,因此它将加倍您的TDD工作,降低您的速度,并使测试更难维护

相反,使用Sql Server Compact Edition进行的集成测试将向您确认,您的业务逻辑不仅可以正常工作,而且可以针对实时数据库正常工作

作为旁注,我谈论的是使用集成测试测试业务逻辑。如果控制器实际直接访问数据库(不应该是imho,MVC控制器应该调用您的业务类来执行DB操作),则只应针对真实数据库测试MVC控制器,如果控制器不直接与数据库对话,那么您可以使用Moq这样的模拟框架对业务类进行内存模拟(因为
[TestMethod]
public void Can_Retrieve_User_Requests()
{
    // Setup
    var user = new User();
    var req1 = new Request();       
    user.Requests.Add(req1);

    _unitOfWork.Add(user);
    _unitOfWork.Add(req1);
    _unitOfWork.Commit();

    // Act
    var result = BusinessLogicClass.GetRequestsByUserId(user.Id);

    // Assert
    Assert.IsNotNull(result);
    Assert.AreEqual(1, result.count);
}