在 ABP vNext 中编写仓储单元测试的问题一则
一、问题
新项目是基于 ABP vNext 框架进行开发的,所以我要求为每层编写单元测试。在同事为某个仓储编写单元测试的时候,发现了一个奇怪的问题。他的对某个聚合根的 A 字段进行了更新,随后对某个导航属性 B 也进行了变更,最后通过仓储提供的 UpdateAsync() 方法对变更的数据进行持久化。
结果再次查出来的时候,发现聚合根的 A 字段倒是更新了,但是导航属性 B 的内部字段没有进行变更。例如在下面的实例当中,聚合根的 Name 字段变更成功,但是导航属性的 Street 字段变更失败了。

二、原因
数据没有更新到,说明问题肯定出在 UpdateAsync 方法内部,通过打断点单步步入之后,也没发现有什么奇怪的地方,是使用的 ABP vNext 提供的默认仓储实现。
又在想是否跟实体追踪有关,然后看同事写得单元测试代码,发现他是先使用的 GetAsync() 方法获取到实体,然后手动变更了实体的属性。变更完成之后,通过仓储提供的 UpdateAsync() 方法进行更新。
看了很久发现它们并不是公用的一个工作单元,这就导致 GetAsync() 和 UpdateAsync() 方法内部得到的 DbContext 是不一样的。在 EF Core 内部针对这种情况,称之为 Disconnected entities 即断开连接的实体,这个时候需要用户手动 Attch 追踪导航属性。
三、解决
所以有两种解决办法,第一种方法是保证使用 GetAsync() 和 UpdateAsync() 方法时,它们都处于一个工作单元下,例如下面的伪代码。
private readonly IUnitOfWorkManager _uowMgr;
private readonly IRepository<TestUser, Guid> _repository;
[Fact]
public async Task Resolve1()
{
// 创建初始数据。
var entityId = Guid.NewGuid();
await _repository.InsertAsync(new TestUser
{
Id = entityId,
Name = "张三",
Address = new TestUserAddress
{
City = "成都市",
Street = "春熙路"
}
});
using (var outerUow = _uowMgr.Begin())
{
var entity = await _repository.GetAsync(entityId);
entity.Name = "李四";
entity.Address.Street = "琴台路";
await _repository.UpdateAsync(entity);
await outerUow.CompleteAsync();
}
// 最后查询街道是否成功修改。
var result = await _repository.GetAsync(entityId);
result.Name.ShouldBe("李四");
result.Address.Street.ShouldBe("琴台路");
}

第二种方法变动则要大一些, 导航属性没有更新的根本原因,是因为在第二个工作单元中没有追踪到这个属性,你只需要手动附加该导航属性即可。在下面的例子中,我们重写了 UpdateAsync() 方法,手动跟踪导航属性,也能够达到上述效果。
public class TestUserRepository : EfCoreRepository<XXXDbContext,TestUser,Guid>
{
public TestUserRepository(IDbContextProvider<XXXDbContext> dbContextProvider) : base(dbContextProvider)
{
}
public override IQueryable<TestUser> WithDetails()
{
return GetQueryable().Include(x => x.Address);
}
public override Task<TestUser> UpdateAsync(TestUser entity, bool autoSave = false, CancellationToken cancellationToken = new CancellationToken())
{
DbContext.Attach(entity.Address).State = EntityState.Modified;
return base.UpdateAsync(entity, autoSave, cancellationToken);
}
}

四、参考资料
- StackOverflow - Entity Framework disconnected graph and navigation property
- MSDN - Disconnected entities
在 ABP vNext 中编写仓储单元测试的问题一则的更多相关文章
- ABP vNext中使用开源日志面板 LogDashboard
ABP vNext 使用 logdashboard 本文示例源码:https://github.com/liangshiw/LogDashboard/tree/master/samples/abpvn ...
- [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)
一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...
- 尝试从零开始构建我的商城 (一) :使用Abp vNext快速一个简单的商城项目
尝试从零开始构建我的商城 (一) :使用Abp vNext快速搭建一个简单的项目 前言 GitHub地址 https://github.com/yingpanwang/MyShop 此文目的 本文将尝 ...
- [Abp vNext微服务实践] - 服务通讯
简介 服务通讯是微服务架构中必不可少的功能,服务通讯的效率决定了微服务架构的优略.常用的微服务通讯策略有两种,分别是rpc.http,其中rpc以gRpc框架为代表使用者最多.abp vNext微服务 ...
- [Abp vNext微服务实践] - 业务开发
前几篇分别介绍了abp vNext微服务框架.开发环境搭建和vue element admin前端框架接入,在vue element admin中实现用户角色管理基本功能后就可以开始进行业务开发了,本 ...
- Abp vnext EFCore 实现动态上下文DbSet踩坑记
背景 我们在用EFCore框架操作数据库的时候,我们会遇到在 xxDbContext 中要写大量的上下文 DbSet<>; 那我们表少还可以接受,表多的时候每张表都要写一个DbSet, 大 ...
- [Abp vNext 源码分析] - 3. 依赖注入与拦截器
一.简要说明 ABP vNext 框架在使用依赖注入服务的时候,是直接使用的微软提供的 Microsoft.Extensions.DependencyInjection 包.这里与原来的 ABP 框架 ...
- Abp vNext抽茧剥丝01 使用using临时更改当前租户
在Abp vNext中,如果开启了多租户功能,在业务代码中默认使用当前租户的数据,如果我们需要更改当前租户,可以使用下面的方法 /* 此时当前租户 */ using (CurrentTenant.Ch ...
- [Abp vNext微服务实践] - 添加中文语言
简介 abp vNext中提供了多语言功能,默认语言是英文,没有提供中文语言包.在业务开发中,定义权限后需要用中文的备注提供角色选择,本篇将介绍如何在abp vNext中加入中文语言. step1:添 ...
随机推荐
- Opengl_入门学习分享和记录_番外篇00(MacOS上如何给Xcode 适配openGL)
现在前面的废话:哇这次没有鸽太久,突然想起来还没有介绍如何适配opengl的衍生库.今天一并介绍下,同样看时间允不允许,让我再把之前学到的一些东西再次总结一遍. 正文开始: 首先大家要知道我们的Ope ...
- c#小灶——常量、变量和赋值
常量 常量很好理解,和变量相对,就是不会变的量.比如,1就是常量,3.6也是常量,‘a’也是常量,“aaaaa”也是常量,只是不同类型.这些都是表面上一眼就看出来的常量,还有一种表面上看不出来的常量, ...
- 使用Prerender.io进行网站预加载
我在自己的项目中是采用的前后端分离的技术,前端用的VUE开发,后端是JAVA开发,tomcat部署,nginx转发,但是VUE开发的项目缺点就是不利于SEO,所以针对SEO做了预加载的操作. 决定采用 ...
- js五子棋游戏代码分享
HTML代码 <canvas id="game"></canvas> CSS代码 * { margin: 0; padding: 0; } #game { ...
- 构建企业级数据湖?Azure Data Lake Storage Gen2不容错过(上)
背景 相较传统的重量级OLAP数据仓库,“数据湖”以其数据体量大.综合成本低.支持非结构化数据.查询灵活多变等特点,受到越来越多企业的青睐,逐渐成为了现代数据平台的核心和架构范式. 数据湖的核心功能, ...
- spring-boot-plus集成Spring Boot Admin管理和监控应用(十一)
spring-boot-plus集成Spring Boot Admin管理和监控应用 spring boot admin Spring Boot Admin用来管理和监控Spring Boot应用程序 ...
- python + selenium webdriver 通过python来模拟鼠标、键盘操作,来解决SWFFileUpload调用系统底层弹出框无法定位问题
Webdriver是基于浏览器操作的,当页面上传文件使用的是flash的控件SWFFileUpload调用的时候,调用的是系统底层的文件选择弹出框 这种情况,Webdriver暂时是不支持除页面外的其 ...
- react native 8081 端口号被占
McAfee Agent 占用8081 端口号 公司电脑被标装,McAfee Agent关不掉 sudo lsof -n -i4TCP:8081 sudo launchctl list | grep ...
- Sqlserver2012 评估期已过问题
sql server 2012提示评估期已过的解决方法: 第一步:进入SQL2012配置工具中的安装中心. 第二步:再进入左侧维护选项界面,然后选择选择版本升级. 第三步:进入输入产品密钥界面,输入相 ...
- 【实践总结】给Centos和Ubuntu设置静态网络IP以及配置ssh功能
作为一名以Windows平台为主的开发者,在接触和使用Linux系统的过程中总会遇到一系列的问题.每当这时候,我相信大部分人是和我一样的处理办法,就是网上各种搜索尝试直到问题解决为止,而有些问题,前后 ...