locked
System.InvalidOperationException Message=The instance of entity type 'QuestionOnTest' cannot be tracked because another instance with the same key value for {'QuestionId', 'TestId'} is already being tracked. RRS feed

  • Question

  • User2005610433 posted

    System.InvalidOperationException
    HResult=0x80131509
    Message=The instance of entity type 'QuestionOnTest' cannot be tracked because another instance with the same key value for {'QuestionId', 'TestId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. 

    I am getting this error when updating a many to many relationship.  The add works OK, and so does the Delete, but when updating I am getting this error.
    I tried many things but all have failed. I tried just updating and then I tried removing and then adding, not sure what to do at this point

    Please Help!!!  Thanks!

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Rendering;
    
    using QA_Project.Models;
    using QA_Project.ViewModels;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using static QA_Project.ViewModels.TestCreateViewModel;
    
    namespace QA_Project.Controllers
    {
        public class TestController : Controller
        {
            private readonly ITestRepository _testRepository;
            private readonly IDepartmentRepository _departmentRepository;
            private readonly IQuestionRepository _questionRepository;
            private readonly IApplicationRepository _applicationRepository;
            private readonly ITestAreaRepository _testAreaRepository;
            private readonly IQuestionOnTestRepository _questionOnTest;
    
            public TestController(ITestRepository testRepository,
                                    IDepartmentRepository departmentRepository,
                                    IQuestionRepository questionRepository,
                                    IApplicationRepository applicationRepository,
                                    ITestAreaRepository testAreaRepository,
                                    IQuestionOnTestRepository questionOnTest)
            {
                this._testRepository = testRepository;
                this._departmentRepository = departmentRepository;
                this._questionRepository = questionRepository;
                this._applicationRepository = applicationRepository;
                this._testAreaRepository = testAreaRepository;
                this._questionOnTest = questionOnTest;
            }
            public IActionResult Index()
            {
                var model = _testRepository.GetAllTests();
    
                ViewData["Title"] = "Tests Page";
                return View(model);
            }
    
            public IActionResult Details(int id)
            {
                TestDetailsViewModel testDetailsViewModel = new TestDetailsViewModel()
                {
                    Test = _testRepository.GetTest(id)
                    ,
                    question = _testRepository.GetQuestions(id)
                };
                return View(testDetailsViewModel);
            }
    
            [HttpGet]
            public IActionResult Create()
            {
                var questionsfromDB = _testRepository.GetQuestions();
                var questionsForModel = new List<TestEditViewModel.AddQuestionsToTest>();
    
                foreach (var question in questionsfromDB)
                {
                    var createmodel = new TestEditViewModel.AddQuestionsToTest();
    
                    createmodel.QuestionID = question.QuestionId;
                    createmodel.Description = question.Description;
                    createmodel.Application = question.Application.ApplicationName;
                    createmodel.TestArea = question.TestArea.TestAreaName;
                    createmodel.IsChecked = false;
    
                    questionsForModel.Add(createmodel);
                }
    
                var model = new TestEditViewModel();
    
                model.Departments = new List<SelectListItem>(
                            new SelectList(_departmentRepository.GetDepartments(), "DepartmentId", "DepartmentName"));
    
                //model.Test = new Test();
                model.TestQuestions = questionsForModel;
    
                ViewData["Title"] = "Test Creation";
                return View(model);
            }
    
            [HttpPost]
            public IActionResult Create(TestEditViewModel testEditViewModel)
            {
    
                if (true
                    //ModelState.IsValid
                    )
                {
                    var qlist = testEditViewModel.TestQuestions.Where(q => q.IsChecked == true).ToList();
               
    
                    var qlist1 = new List<Question>();
    
                    QuestionOnTest QonT1 = new QuestionOnTest();
            
    
                    Test test = new Test();
    
                    foreach (var ql in qlist)//take info from qlist and make "questions" to add to questionlist
                    {
                        qlist1.Add(_questionRepository.GetQuestion(ql.QuestionID));
                    }
    
    
                    test.Department = _departmentRepository.GetDepartment(testEditViewModel.DepartmentId);
                    test.TestName = testEditViewModel.TestName;
                    test.DateTestAdded = DateTime.Now;
    
                    // test.Questions.Add
                    //https://stackoverflow.com/questions/38893873/saving-many-to-many-relationship-in-entity-framework-core
                    //see this
    
                    QonT1.Test = test;
    
    
                    foreach (var q in qlist1)
                    {
    
                        QonT1.Question = _questionRepository.GetQuestion(q.QuestionId);
                        QonT1.Test = test;
                        _questionOnTest.Add(QonT1);
    
                    }
                    return RedirectToAction("Index");
                }
                return View();
            }
    
            [HttpGet]
            public IActionResult Edit(int id)
            {
                Test test = _testRepository.GetTest(id);
                var questions = _testRepository.GetQuestions(id);
                var allquestions = _testRepository.GetQuestions().Except(questions);
    
                TestEditViewModel editTest = new TestEditViewModel();
                var questionsForModel1 = new List<TestEditViewModel.AddQuestionsToTest>();
                var questionsForModel2 = new List<TestEditViewModel.AddQuestionsToTest>();
    
                editTest.TestId = test.TestId;
                editTest.TestName = test.TestName;
                editTest.Departments = new List<SelectListItem>(
                    new SelectList(_departmentRepository.GetDepartments(), "DepartmentId", "DepartmentName",
                    _departmentRepository.GetDepartment(test.Department.DepartmentId)));
    
                foreach (Question q in questions)
                {
                    var AQTT = new TestEditViewModel.AddQuestionsToTest();
    
                    AQTT.QuestionID = q.QuestionId;
                    AQTT.Application = q.Application.ApplicationName;
                    AQTT.TestArea = q.TestArea.TestAreaName;
                    AQTT.Description = q.Description;
                    AQTT.IsChecked = true;
    
                    questionsForModel1.Add(AQTT);
                }
    
                foreach (Question q in allquestions)
                {
                    var AQTT = new TestEditViewModel.AddQuestionsToTest();
    
                    AQTT.QuestionID = q.QuestionId;
                    AQTT.Application = q.Application.ApplicationName;
                    AQTT.TestArea = q.TestArea.TestAreaName;
                    AQTT.Description = q.Description;
                    AQTT.IsChecked = false;
    
                    questionsForModel2.Add(AQTT);
                }
    
                var questionsForModelFinal = new List<TestEditViewModel.AddQuestionsToTest>();
                    questionsForModelFinal= questionsForModel1.Union(questionsForModel2).ToList();
    
    
                editTest.TestQuestions = questionsForModelFinal;
    
                return View(editTest);
            }
            [HttpPost]
            public IActionResult Edit(TestEditViewModel testEditViewModel)
            {
                if (true
                        //ModelState.IsValid
                    )
                {
                    //_questionOnTest.RemoveAll(testEditViewModel.TestId);
    
                    var qlist = testEditViewModel.TestQuestions.Where(q => q.IsChecked == true).ToList();
    
                    var qlist1 = new List<Question>();
    
                    QuestionOnTest QonT1 = new QuestionOnTest();
               
    
                    foreach (var ql in qlist)//take info from qlist and make "questions" to add to questionlist
                    {
                        qlist1.Add(_questionRepository.GetQuestion(ql.QuestionID));
    
                    }
    
                    Test test = _testRepository.GetTest(testEditViewModel.TestId);
                    test.Department = _departmentRepository.GetDepartment(testEditViewModel.DepartmentId);
                    test.TestName = testEditViewModel.TestName;
    
    
                    //https://stackoverflow.com/questions/38893873/saving-many-to-many-relationship-in-entity-framework-core
                    //see this
    
                    List<QuestionOnTest> qolist = _questionOnTest.GetQuestionsOnTest(test.TestId).ToList();
    
                        //_questionOnTest.GetQuestionsOnTest(testEditViewModel.TestId).ToList();
                    
                    foreach(var q in qolist)
                    {
                       // _questionOnTest.Remove(test.TestId, q.QuestionId);
                    }
    
    
    
                    foreach (var q in qlist1)
                    {
                        QonT1.Question = _questionRepository.GetQuestion(q.QuestionId);
                        QonT1.Test = test;
                        _questionOnTest.Update(QonT1);
                    }
    
                    return RedirectToAction("Index");
                }
    
    
            }
    
    
    
    
           public IActionResult Delete(int id)
            {
                _testRepository.Delete(id);
                return RedirectToAction("Index");
            }
        
        }
    }
    
    using Microsoft.EntityFrameworkCore;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace QA_Project.Models
    {
        public class SQLQuestionOnTest : IQuestionOnTestRepository
        {
            private readonly AppDbContext dbContext;
    
            public SQLQuestionOnTest(AppDbContext dbContext)
            {
                this.dbContext = dbContext;
            }
    
            public QuestionOnTest Add(QuestionOnTest questionOnTest)
            {
    
                dbContext.Add(questionOnTest);
                dbContext.SaveChanges();
                return questionOnTest;
            }
    
            public IEnumerable<QuestionOnTest> GetQuestionsOnTest(int id)
            {
              return  dbContext.QuestionOnTest.Where(t => t.TestId == id).ToList();
            }
    
    
            public QuestionOnTest Update(QuestionOnTest questionOnTest)
            {
                //https://www.google.com/search?q=update+many+to+many+relationship+entity+framework&oq=update+many+to+many+r&aqs=chrome.1.69i57j0l5.9319j0j4&sourceid=chrome&ie=UTF-8
                //var QONT = dbContext.QuestionOnTest.Attach(questionOnTest);
                //var Test = dbContext.Tests.Attach( dbContext.Tests.Find(questionOnTest.TestId));
                //QONT.State = EntityState.Modified;
                //Test.State = EntityState.Modified;
    
               // var test = dbContext.Tests.FirstOrDefault(t => t.TestId == questionOnTest.Test.TestId);
                //var questions = dbContext.QuestionOnTest.Where(t => t.TestId == questionOnTest.Test.TestId);
    
                //dbContext.QuestionOnTest.Remove(questionOnTest);
    
                dbContext.Entry(questionOnTest).State = EntityState.Modified;
                dbContext.Update(questionOnTest);
    
                //Add(questionOnTest);
    
                dbContext.SaveChanges();
                return questionOnTest;
            }
    
            public  void Remove(int tid, int qid)
            {
                var test = dbContext.Tests.AsNoTracking().FirstOrDefault(t=>t.TestId==tid);
    
                var question =  dbContext.Questions.AsNoTracking().FirstOrDefault(q=>q.QuestionId==qid);
    
                QuestionOnTest qont = new QuestionOnTest();
                
                    qont.Question = dbContext.Questions.Find(qid);
                    qont.Test = dbContext.Tests.Find(tid);
    
                
    
                dbContext.Remove(qont);
                dbContext.SaveChanges();
            
            }
    
        }
    }
    
    using Microsoft.EntityFrameworkCore;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace QA_Project.Models
    {
        public class SQLTestRepository : ITestRepository
        {
            private readonly AppDbContext dbContext;
    
            public SQLTestRepository(AppDbContext dbContext)
            {
                this.dbContext = dbContext;
            }
            public Test Add(Test test)
            {
                dbContext.Add(test);
                dbContext.SaveChanges();
                return test;
            }
    
            public Test Delete(int Id)
            {
                Test test = dbContext.Tests.Find(Id);
                if (test != null)
                {
                    dbContext.Tests.Remove(test);
                    dbContext.SaveChanges();
                }
    
                return test;
            }
    
            public IEnumerable<Test> GetAllTests()
            {
                return dbContext.Tests
                    .Include(test => test.QuestionsOnTests).Include(test => test.Department);
                
            }
            public IEnumerable<Question> GetQuestions(int id)
            {
                var QoNT = dbContext.QuestionOnTest.Where(qon => qon.TestId == id).ToList();
               
    
                var quest = new List<Question>();
    
                foreach (QuestionOnTest q in QoNT)
                {
                    var ques = new Question();
    
                    ques = dbContext.Questions.Where(a => a.QuestionId == q.QuestionId).
                        Include("TestArea").Include("Application").FirstOrDefault();
                    quest.Add(ques);
                }
    
    
                return quest;
                
            }
            public IEnumerable<Question> GetQuestions()
            {
                return dbContext.Questions.Include("TestArea").Include("Application");
    
            }
    
            public Test GetTest(int Id)
            {
                var test = dbContext.Tests.Where(t => t.TestId == Id)
                    .Include(t => t.QuestionsOnTests)
                    .Include(t=>t.Department).FirstOrDefault();
    
                return test;
            }
    
            public Test Update(Test testChanges)
            {
                throw new NotImplementedException();
            }
        }
    }
    
    using Microsoft.EntityFrameworkCore;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace QA_Project.Models
    {
        public class AppDbContext:DbContext
        {
            public AppDbContext(DbContextOptions<AppDbContext>options):base(options)
            { }
    
            public DbSet<Question> Questions { get; set; }
            public DbSet<Test> Tests { get; set; }
    
            public DbSet<Department> Departments { get; set; }
            public DbSet<Application> Applications { get; set; }
    
            public DbSet<TestArea> TestAreas { get; set; }
            public DbSet<QuestionOnTest> QuestionOnTest { get; set; }
    
            public DbSet<QuestionAnswered> QuestionsAnswered { get; set; }
            public DbSet<TestTaken> TestsTaken { get; set; }
            public DbSet<ReleaseTestGroup> ReleaseTestGroups { get; set; }
    
            public DbSet<TestOnRelease> testsOnRelease { get; set; }
    
    
    
            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
    
                modelBuilder.Entity<Question>().HasKey(q => new { q.QuestionId });
    
                modelBuilder.Entity<Test>().HasKey(t => new { t.TestId });
    
                modelBuilder.Entity<QuestionOnTest>()
                    .HasKey(x => new { x.QuestionId, x.TestId });
    
                modelBuilder.Entity<QuestionOnTest>()
                    .HasOne(q => q.Question)
                    .WithMany(t => t.QuestionsOnTests)
                    .HasForeignKey(f => f.QuestionId);
    
                modelBuilder.Entity<QuestionOnTest>()
                    .HasOne(t => t.Test)
                    .WithMany(q => q.QuestionsOnTests)
                    .HasForeignKey(f => f.TestId);
    
                modelBuilder.Entity<Department>()
                 .HasKey(q => new { q.DepartmentId });
    
                modelBuilder.Entity<Application>()
                 .HasKey(q => new { q.ApplicationId });
    
                modelBuilder.Entity<TestArea>()
                  .HasKey(ta => new { ta.TestAreaId });
    
                modelBuilder.Entity<TestOnRelease>()
                    .HasKey(x => new { x.ReleaseTestGroupId, x.TestId });
    
                modelBuilder.Entity<TestOnRelease>()
                    .HasOne(t => t.Test)
                    .WithMany(r => r.TestsOnRelease)
                    .HasForeignKey(t => t.TestId);
    
                modelBuilder.Entity<TestOnRelease>()
                    .HasOne(r => r.ReleaseTestGroup)
                    .WithMany(t => t.TestsOnRelease)
                    .HasForeignKey(r => r.ReleaseTestGroupId);
    
            }
        }
    }
    

    Thursday, October 10, 2019 4:46 AM

All replies

  • User1120430333 posted

    It seems to me that you're using one context the 'dbcontext' to do two different operations consecutively. Dbcontext is being used in an update, and right in the middle of the update,  you do  a remove and save, come  back to the update and do another save using the same 'dbcontext'. I don't think that's viable not to EF anyway. 

    IMO, you need two different contexts dbcontextudp and dbcontextdel, or you refactor the code so that update and delete logic is not being executed within the same scope using the single dbcontext.

    Thursday, October 10, 2019 9:45 AM
  • User2005610433 posted

    In my example in the TestController I commented out the Remove method, I am only trying to update.

                    foreach(var q in qolist)
                    {
                       // _questionOnTest.Remove(test.TestId, q.QuestionId);
                    }
    

    I do not want to remove that is just something I tried and it is not working for me. I would rather not remove, but just update if possible.

    I can add a second DBContext if you like.  Do you mean instantiate another instance?
    Any ideas?

    Friday, October 11, 2019 4:54 AM
  • User2005610433 posted

    Instan

            private readonly AppDbContext dbContext;
            private readonly AppDbContext dbContext2;
    
            public SQLQuestionOnTest(AppDbContext dbContext, AppDbContext dbContext2)
            {
                this.dbContext = dbContext;
                this.dbContext2 = dbContext;
            }
    
            public QuestionOnTest Add(QuestionOnTest questionOnTest)
            {
    
                dbContext.Add(questionOnTest);
                dbContext.SaveChanges();
                return questionOnTest;
            }
    
            public IEnumerable<QuestionOnTest> GetQuestionsOnTest(int id)
            {
              return  dbContext.QuestionOnTest.Where(t => t.TestId == id).ToList();
            }
    
    
            public QuestionOnTest Update(QuestionOnTest questionOnTest)
            {
                //https://www.google.com/search?q=update+many+to+many+relationship+entity+framework&oq=update+many+to+many+r&aqs=chrome.1.69i57j0l5.9319j0j4&sourceid=chrome&ie=UTF-8
                //var QONT = dbContext.QuestionOnTest.Attach(questionOnTest);
                //var Test = dbContext.Tests.Attach( dbContext.Tests.Find(questionOnTest.TestId));
                //QONT.State = EntityState.Modified;
                //Test.State = EntityState.Modified;
    
               // var test = dbContext.Tests.FirstOrDefault(t => t.TestId == questionOnTest.Test.TestId);
                //var questions = dbContext.QuestionOnTest.Where(t => t.TestId == questionOnTest.Test.TestId);
    
                //dbContext.QuestionOnTest.Remove(questionOnTest);
    
                dbContext2.Entry(questionOnTest).State = EntityState.Modified;//I get the Error here
                dbContext2.Update(questionOnTest);
    
                //Add(questionOnTest);
    
                dbContext.SaveChanges();
                return questionOnTest;
            }
    


    tiated another dbContext, but got the same error.

    Friday, October 11, 2019 5:03 AM
  • User1120430333 posted

    You may be able to create two different and separate context objects by using the following code from the link, take note on how to use the constructor that is documented in the link and comment out the code for the connectionstring in the OnConfiguration method in the context object, becuase the constructor method provides the connectionstring. 

    https://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext

    Application code to initialize from constructor argument:
    C#
    
    
    var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
    optionsBuilder.UseSqlite("Data Source=blog.db");
    
    using (var context = new BloggingContext(optionsBuilder.Options))
    {
      // do stuff
    }

    Friday, October 11, 2019 8:16 AM
  • User-17257777 posted

    Hi lukasamen,

    I can't reproduce your issue, since there is still a lot of information not given. You can show us  the complete code that has the problem including model, view and controller.

    Best Regards,

    Jiadong Meng

    Friday, October 11, 2019 10:10 AM