在 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:添 ...
随机推荐
- Spider & CrawlSpider
CrawlSpide 最重要的是分析源码官方文档百度收搜 Spider document 就是官方文档了1.3的源码分析CrawlSpide 是爬取一个网站常用的规则 它是对spider进一步的包装 ...
- java中什么是继承笔记
继承 怎样实现继承:1,先提取共有的属性和方法,放到一个类里,这个叫父类.基类.超类 2.编写子类 修饰符 class 子类名 extends 父类名 好处:提高代码的复用性 子类怎么去 ...
- mui的app页面使用layui填充数据
在mui的开发中有个坑,mui.plusReady在web上使用时是不会起作用的,只能在app上才行,所以推荐自己测试时使用mui.ready去写加载时的方法. 前端请求的返回格式为json,所以在后 ...
- Missing artifact XXXXX:jar:1.9.1 解决错误问题
昨天导过来一个maven工程的一个项目,由于自己meven库中有许多现成的jar包,但是还是有一些需要去下载的,配置的是阿里云的镜像,把eclise的预编译给关闭,具体做法为:Project----- ...
- JS鼠标吸粉特效
HTML <canvas id=canvas></canvas> CSS * { margin: 0; padding: 0; } html { overflow: hidde ...
- SAP Special Fields in FAGLL03 transaction
https://wiki.scn.sap.com/wiki/display/ERPFI/Special+Fields+in+FAGLL03+transaction https://wiki.scn.s ...
- var let及const
es6已经发布很久了,之前只会用var定义变量,学习了let和const后,又学到了一些作用域.JavaScript编译和深拷贝浅拷贝的知识.这章主要来说说这三个定义量的方法: 1.var 在没学习e ...
- 亲,麻烦给个五星好评!—RatingBar
引言 上一篇的CheckBox已经让大家越来越接近实战演练了,本章我们继续分享干货给大家,今天介绍一个实用的UI控件RatingBar(星级评分条),对于使用过电商APP(某东,某宝等)的小伙伴们来说 ...
- HackerRank - maximum-gcd-and-sum
题意:给你两个等长的数列,让你在两个数列中各选择一个数字,使得这两个数的gcd是这n * n种组合中最大的. 思路:如果上来就考虑分解因式什么的,就想偏了,假设数列1的最大数为max1,数列2的最大数 ...
- 2019年7-8月Leetcode每日训练日志
2019-08-29 #274 H指数 2019-08-28 #287 寻找重复数 #875 爱吃香蕉的珂珂 #704 二分查找 2019-08-27 #744 寻找比目标字母大的最小字母 #225 ...