Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I'm trying to write a unit test which uses the inmemorydbcontext of entity framework core:

Setup of the 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;
        } 

my Unit Test:

[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());
    }

The method to be tested:

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);
        }

The test fails at the line: db.AddOrUpdate(projektStatus); with this message:

System.InvalidOperationException: The instance of entity type 'PPMProjektStatus' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.

Why is it throwing this exception? It only fails in the unit test, it works when I run the program manually. I explicitely used .AsNoTracking() to avoid this issue but it seems like the InMemoryDatabase doesn't care about it...

Thanks in advance

Edit: I've also tried to only load the ID instead of the whole object like this

            var oldProjektStatus = db.PPMProjektStatus.SingleOrDefault(x => x.Id == projektStatus.Id)?.PhaseStatusId;

but I still get the same error...

question from:https://stackoverflow.com/questions/66065877/entity-framework-core-unit-test-entity-cannot-be-tracked

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
336 views
Welcome To Ask or Share your Answers For Others

1 Answer

Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...