Home > front end >  ASP.Net Razor Pages: InvalidOperationException: The instance of entity type 'Category' can
ASP.Net Razor Pages: InvalidOperationException: The instance of entity type 'Category' can

Time:12-14

I am following a tutorial on how to create a website with razor pages and I want to use all data from my categories table to implement custom error checking for my "Edit Category" Page. Though when calling ToListAsync() to retrieve my table to loop through it throws this error when Update() is subsequently called within OnPost() for the category being edited:

InvalidOperationException: The instance of entity type 'Category' 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. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.ThrowIdentityConflict(InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap<TKey>.Add(TKey key, InternalEntityEntry entry, bool updateDuplicate)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, bool acceptChanges, bool modifyProperties)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, bool acceptChanges, bool modifyProperties, Nullable<EntityState> forceStateWhenUnknownKey, Nullable<EntityState> fallbackState)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode<ValueTuple<EntityState, EntityState, bool>> node)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph<TState>(EntityEntryGraphNode<TState> node, Func<EntityEntryGraphNode<TState>, bool> handleNode)
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, bool forceStateWhenUnknownKey)
Microsoft.EntityFrameworkCore.Internal.InternalDbSet<TEntity>.SetEntityState(InternalEntityEntry entry, EntityState entityState)
Microsoft.EntityFrameworkCore.Internal.InternalDbSet<TEntity>.Update(TEntity entity)
Scratch.Pages.EditCategoryModel.OnPost() in EditCategory.cshtml.cs
 
                _db.Category.Update(Category);

Here is my code for context:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Scratch.Model;
using System.Runtime.Serialization;

namespace Scratch.Pages
{
    [BindProperties]
    public class EditCategoryModel : PageModel
    {
        private readonly ApplicationDbContext _db;
        public Category Category { get; set; }

        public EditCategoryModel(ApplicationDbContext db)
        {
            _db = db;
        }

        public IEnumerable<Category> categories { get; set; }
        
        

        public async Task OnGet(int Id)
        {
            categories = await _db.Category.ToListAsync();
            //the category to be edited
            Category = _db.Category.Find(Id);
            



        }

        public async Task<IActionResult> OnPost()
        {



            // categories is reset since OnPost() was called, so i need to call it again.
            //but this causes "_db.Category.Update(Category);" to crash the webpage (i assume due to loading the subject category twice?)
            categories = await _db.Category.ToListAsync();
            




            bool duplicate_name = false;
            foreach (Category cat in categories)
            {
                //not counting the name of the subject category
                if (cat.categoryName.Equals(Category.categoryName) && cat.Id != Category.Id)
                {
                    duplicate_name = true;
                    break;
                }
            }



            if (duplicate_name)
            {
                ModelState.AddModelError("Category.categoryName", "categoryName already exists");
            }

            


            if (ModelState.IsValid)
            {
                //max/min for displayOrder
                if (Category.displayOrder > categories.Count())
                {
                    Category.displayOrder = categories.Count();
                }
                if (Category.displayOrder < 0)
                {
                    Category.displayOrder = 0;
                }
                //i also want to shift all display order indexes down the list if there is not already a vacant index
                int displayOrder = Category.displayOrder;
                bool vacantIndex = true;
                foreach (Category cat in categories)
                {
                    if (cat.displayOrder == displayOrder && cat != Category)
                    {
                        vacantIndex = false; break;
                    }
                }

                if (!vacantIndex)
                {
                    foreach (Category cat in categories)
                    {
                        if (cat.displayOrder <= displayOrder && cat != Category)
                        {
                            cat.displayOrder  = 1;
                            _db.Category.Update(cat);

                        }
                    }
                }
                


                _db.Category.Update(Category);
                await _db.SaveChangesAsync();
                return RedirectToPage("/Category Display/Index");
            }
            return Page();
        }
    }
}

I suppose I'm asking if I can "untrack" the duplicate Category instance or if I can somehow "load" the categories table/list without the duplicate instance. Note that I am also potentially updating the rest of the category instances when shifting them down in the displayOrder.


Update:

this fixed my issue:

                //this substitutes:
                //_db.Category.Update(Category);
                //because the tracking of an instance with the same ID as category began when calling _db.Category.ToListAsync()
                foreach (Category cat in categories)
                {
                    if (cat.Id == Category.Id)
                    {
                        
                        cat.displayOrder = displayOrder;
                        cat.categoryName = Category.categoryName;
                        
                        //works without update!
                        //_db.Category.Update(cat);
                        break;
                    }
                }

CodePudding user response:

Unless you have disabled tracking. Retrieved entities are being tracked, so calling Update is trying to track an entity that is already being tracked. That's why you don't really need to call it. And SaveChangesAsync will be enough to persist the update you made in the database.

  • Related