.net - 无法跟踪 Entity Framework Core 单元测试实体
问题描述
我正在尝试编写一个使用实体框架核心的 inmemorydbcontext 的单元测试:
InMemoryDbContext 的设置:
public abstract class ServiceTestsBase<TContextInterface, TContext>
where TContextInterface : IDbContext
where TContext : DbContext, TContextInterface
{
protected TContextInterface InMemoryContext { get; private set; }
public ServiceTestsBase()
{
this.InitializeInMemoryContext();
}
protected void InitializeInMemoryContext()
{
var httpContextAccessorFake = new Mock<IHttpContextAccessor>();
var randomAuditDatabaseName = $"{nameof(IAuditingContext)}_{Guid.NewGuid()}";
var auditOptions = new DbContextOptionsBuilder<AuditDbContext>().UseInMemoryDatabase(randomAuditDatabaseName).Options;
var auditContext = new AuditDbContext(auditOptions);
var randomDatabaseName = $"{nameof(TContextInterface)}_{Guid.NewGuid()}";
var options = new DbContextOptionsBuilder<TContext>().UseInMemoryDatabase(randomDatabaseName).Options;
this.InMemoryContext = (TContext) Activator.CreateInstance(typeof(TContext), options, httpContextAccessorFake.Object, auditContext);
}
protected int AddToInMemoryContext<TEntity>(ICollection<TEntity> entities) where TEntity : BaseEntity
{
foreach (var entity in entities)
{
this.InMemoryContext.Add(entity);
}
return this.InMemoryContext.SaveChangesAsync().Result;
}
我的单元测试:
[TestMethod]
public void SaveProjektStatus_WhenOldPhaseStatusWasAbgebrochenAndNewStatusIsAbgebrochen_ThenShouldNotSendMail()
{
var ppmProjektId = new Guid("0A1D9BCF-01BA-418E-81D8-FD5EFFD79046");
var projektStatusId = new Guid("4600DEEF-09DB-4CB3-99CF-4D3BCBA22712");
var ppmProjekt = new PPMProjekt
{
Id = ppmProjektId
};
var oldProjektStatus = new PPMProjektStatus
{
Id = projektStatusId,
PPMProjektId = ppmProjektId,
PhaseStatusId = PPMCodeConst.PPM_PHASESTATUS_ABGEBROCHEN.Id
};
this.AddToInMemoryContext(new[] { ppmProjekt });
this.AddToInMemoryContext(new[] { oldProjektStatus });
var newProjektStatus = new PPMProjektStatus
{
Id = projektStatusId,
PPMProjektId = ppmProjektId,
PhaseStatusId = PPMCodeConst.PPM_PHASESTATUS_ABGEBROCHEN.Id
};
emailServiceFake.Setup(x => x.GetEmailTemplateById(PPMEmailVorlageConst.PPM_PROJECT_STATUS_CHANGED.Id))
.Returns(new EmailVorlage
{
SubjectDt = "{0}{1}",
BodyDt = "{0}{1}",
DefaultEmpfaenger = "testmail@test.com"
});
this.testee.SaveProjektStatus(newProjektStatus);
emailServiceFake.Verify(x => x.SendEmail(It.IsAny<EMail>()), Times.Never());
}
待测方法:
public void SaveProjektStatus(PPMProjektStatus projektStatus)
{
var oldProjektStatus = db.PPMProjektStatus.AsNoTracking().SingleOrDefault(x => x.Id == projektStatus.Id);
var personalInternTagessatz = db.PPMProjekt.Single(x => x.Id == projektStatus.PPMProjektId).PersonalInternTagessatz_CHF;
projektStatus.PersonalInternFachIST_CHF = personalInternTagessatz * projektStatus.PersonalInternFachIST_PT;
projektStatus.PersonalInternFachHochrechnung_CHF = personalInternTagessatz * projektStatus.PersonalInternFachHochrechnung_PT;
projektStatus.PersonalInternInformatikIST_CHF = personalInternTagessatz * projektStatus.PersonalInternInformatikIST_PT;
projektStatus.PersonalInternInformatikHochrechnung_CHF = personalInternTagessatz * projektStatus.PersonalInternInformatikHochrechnung_PT;
db.AddOrUpdate(projektStatus);
db.SaveChanges();
if (projektStatus.PhaseStatusId.Equals(PPMCodeConst.PPM_PHASESTATUS_ABGEBROCHEN.Id)
&& (oldProjektStatus == null || !oldProjektStatus.PhaseStatusId.Equals(PPMCodeConst.PPM_PHASESTATUS_ABGEBROCHEN.Id)))
{
NotifyProjectStatusAbgebrochen(projektStatus.PPMProjekt.Nummer, projektStatus.PPMProjekt.Bezeichnung, projektStatus.GeaendertAm);
}
if (projektStatus.PhaseStatusId.Equals(PPMCodeConst.PPM_PHASESTATUS_UEBERFUERTIN.Id))
{
var projektUeberfuertIn = db.PPMProjekt.Where(x => x.Id == projektStatus.ProjektUeberfuertIn).FirstOrDefault();
NotifyProjectStatusUeberfuertIn(projektStatus.PPMProjekt.Nummer, projektStatus.PPMProjekt.Bezeichnung, projektStatus.GeaendertAm, projektUeberfuertIn.Nummer, projektUeberfuertIn.Bezeichnung);
}
searchService.RebuildProjekteSearchIndex(projektStatus.PPMProjektId);
}
测试在以下行失败:db.AddOrUpdate(projektStatus); 与此消息:
System.InvalidOperationException:无法跟踪实体类型“PPMProjektStatus”的实例,因为已经在跟踪具有相同键值 {'Id'} 的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。
为什么会抛出这个异常?它只在单元测试中失败,当我手动运行程序时它可以工作。我明确地使用 .AsNoTracking() 来避免这个问题,但似乎 InMemoryDatabase 并不关心它......
提前致谢
编辑:我也尝试只加载 ID 而不是像这样的整个对象
var oldProjektStatus = db.PPMProjektStatus.SingleOrDefault(x => x.Id == projektStatus.Id)?.PhaseStatusId;
但我仍然得到同样的错误......
解决方案
推荐阅读
- azure - Azure 应用服务中的流文件丢失字节
- javascript - 无效的正则表达式组
- c# - 验证 ADFS 帐户活动 C#
- python - 如何使用 pywinauto 使窗口(已经运行的任务)可见?
- c++ - 如何正确设置 SonarQube cfamil.gcov?
- r - 运行回归并将模型估计提取到 R 中的数据框
- node.js - 错误:节点中的肥皂客户端的 XML 格式无效(联邦快递)
- ldap - Apache 2.4.7 setenvif 为 require 指令构建 LDAP 组名
- matlab - 在 Matlab 的工作区中更改变量的名称
- build - _work 目录中的增量