简体   繁体   中英

The instance of entity type 'Bookmark' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked

I have this bookmark that has a relation with a category, so every time I update an existing bookmark, a new category should be created or updated if it already exists. When I update an existing bookmark I get this error:

The instance of entity type 'Bookmark' 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.

My Entity looks like this:

public class Bookmark
{
    [Key]
    public int ID { get; set; }

    [StringLength(maximumLength: 500)]
    public string URL { get; set; }

    public string ShortDescription { get; set; }

    public int? CategoryId { get; set; }

    public virtual Category Category { get; set; }

    [DataType(DataType.Date)]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
    public DateTime CreateDate { get; set; }
}

My Update Controller looks like this:

public async Task<IActionResult> Edit(BookmarkViewModel request)
    {
        var bookmark = _mapper.Map<Bookmark>(request);

        var exists = await _bookmarkService.GetBookmark(bookmark.ID);
        if(exists == null)
            return new StatusCodeResult(Microsoft.AspNetCore.Http.StatusCodes.Status404NotFound);

        if (ModelState.IsValid)
        {
            if (request.CategoryName != null)
            {
                 var category = _categoryService.CreateCategory(new Category
                {
                    Name = request.CategoryName
                });

                bookmark.Category = category;
                bookmark.CategoryId = category.ID;
            }
            await _bookmarkService.UpdateBookmark(bookmark);

            return RedirectToAction("Index");
        }

        return View(bookmark);
    }

My CreateCategory service looks like this:

    public Category CreateCategory(Category category)
    {
        category.UserId = _identityService.GetUserId();

        var existing = _ReadLaterDataContext.Categories
            .FirstOrDefault(x => x.Name.Equals(category.Name) && x.UserId.Equals(category.UserId));
        if (existing != null)
            return existing;

        _ReadLaterDataContext.Add(category);
        _ReadLaterDataContext.SaveChanges();
        return category;
    }

And my UpdateBookmark service looks like this:

public async Task UpdateBookmark(Bookmark bookmark)
    {
        bookmark.CreateDate = DateTime.Now;

        UpdateExistingCategory(bookmark);
        _ReadLaterDataContext.Update(bookmark);
        await _ReadLaterDataContext.SaveChangesAsync();
    }

My GetBookmark() method

public async Task<Bookmark> GetBookmark(int Id)
    {
        return await _ReadLaterDataContext.Bookmark.Where(x => x.ID == Id && x.UserId.Equals(_identityService.GetUserId())).Include(x => x.Category).FirstOrDefaultAsync();
    }

The problem is here:

await _bookmarkService.UpdateBookmark(bookmark);

This is causing an Identity Resolution violation. Here's an excerpt from the link(with added emphasis):

A DbContext can only track one entity instance with any given primary key value. This means multiple instances of an entity with the same key value must be resolved to a single instance.

The change tracker begins to track the Bookmark soon after the execution of this statement:

var exists = await _bookmarkService.GetBookmark(bookmark.ID);

Change tracking is a feature of EF Core wherein any changes made to an entity is tracked after it is fetched from the database. All these changes will be applied as soon as you call context.SaveChanges() .

Now, if EF Core sees any other Bookmark instance other than the instance in exists with the same primary key, it throws an error. In your case:

var bookmark = _mapper.Map<Bookmark>(request);

In the above statement, bookmark is another instance of Bookmark with the same primary key( Bookmark.ID ). This ID is being set from the post request during model binding. So, in order to get around this, you have to either disable change tracking using AsNoTracking() :

public async Task<Bookmark> GetBookmark(int Id) 
    => await _ReadLaterDataContext.Bookmark
        .AsNoTracking()
        .Where(x => x.ID == Id 
             && x.UserId.Equals(_identityService.GetUserId()))
        .Include(x => x.Category)
        .FirstOrDefaultAsync();
    

Or, the better option would be to make changes in the same instance that was fetched from the database(in your case, exists ). You are also assigning the newly created category to bookmark.Category without checking if the Bookmark already contains another category. Check if the bookmark already contains a category, then change the name of this category if it exists and create a new one only if there is no existing category.

It also appears that you do not need the mapped bookmark, so I've completely gotten rid of it and renamed exists to bookmark . I've assumed that the request contains a property ID . Use the property that contains the bookmark ID:

public async Task<IActionResult> Edit(BookmarkViewModel request)
{
    var bookmark = await _bookmarkService.GetBookmark(request.ID);
    if (bookmark == null)
        return new StatusCodeResult(Microsoft.AspNetCore.Http.StatusCodes.Status404NotFound);

    if (ModelState.IsValid)
    {
        if (request.CategoryName is not null)
        {
            if (bookmark.Category is not null)
            {
                bookmark.Category.Name = request.CategoryName;
            }

            else
            {
                var category = _categoryService.CreateCategory(new Category
                {
                    Name = request.CategoryName
                });

                bookmark.Category = category;
                bookmark.CategoryId = category.ID;
            }
        }
        await _bookmarkService.UpdateBookmark(bookmark);

        return RedirectToAction("Index");
    }

    return View(request);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

Related Question The instance of entity type 'Entity' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked The instance of entity type <T> cannot be tracked because another instance with the same key value for {'Id'} is already being tracked The instance of entity type Model cannot be tracked because another instance with the same key value for {'Id'} is already being tracked The instance of entity type ‘Bus’ cannot be tracked because another instance with the same key value for {‘Id’} is already being tracked The instance of entity type 'AppUser' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked The instance of entity type 'IdentityUser' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked The instance of entity type 'Item' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked The instance of the entity type cannot be tracked because another instance with the same key value pair for{'Id'} is already being tracked The instance of entity type 'Article' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked The instance of entity type 'User' cannot be tracked because another instance with the same key value for 'Id' is already being tracked
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM