Невозможно удалить элемент из бд

214
03 января 2020, 22:30

Возник вопрос.
Во время добавления элемента (объекта) в БД всё работает и отображается как положено.
Но как только возникает необходимость удалить элемент из БД.
Возникает следующая ошибка:

InvalidOperationException: The instance of entity type 'UserRole' cannot be tracked because another instance with the same key value for {'RoleId', 'UserId'} 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.

Интерфейс пользователя выглядит следующим образом:

Фрагмент проблемного кода представлен ниже:

 [Authorize(Roles = "Admin, SuperAdmin")]
    public IActionResult Index()
    {
        var result = (from user in db.Users
                      from userRole in user.UserRoles
                      where userRole.Role.UserRole == UserRole.User
                      orderby user.FullName ascending
                      select user);
        List<Person> persons = new List<Person>();
        //Проставить всех пользователей в checked, если у них есть роль UserRole.ProjectMgr
        foreach (User user in result)
        {
            Person p = new Person { UserId = user.UserID, FullName = user.FullName, Mail = user.Mail };
            db.Entry(user).Collection(u => u.UserRoles).Query()
                .Include(ur => ur.Role)
                .Load();
            bool @checked = user.UserRoles.Any(ur => ur.Role.UserRole == UserRole.ProjectMgr);

            if (@checked)
                p.Checked = @checked;
            persons.Add(p);
        }
        return View(persons);
    }

    //Отметка людей как ProjectMgr
    [Authorize(Roles = "Admin, SuperAdmin")]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> SetProjectMgr(int[] checkProjectMgr)
    {
        //Присвоим роль пользователя при регистрации
        Role role = db.Roles.FirstOrDefault(r => r.UserRole == UserRole.ProjectMgr);
        foreach (User user in db.Users)
        {
            //Подгружаем роли пользователя
            db.Entry(user).Collection(u => u.UserRoles)
                .Query()
                .Include(ur => ur.Role)
                .Load();
            //для роли пользователя
            MSIE.Data.ManyToManyEntities.UserRole userRole = new Data.ManyToManyEntities.UserRole
            {
                Role = role,
                User = user
            };
            //Проверяем наличие роли ProjectMgr
            bool isInRole = user.UserRoles.Any(ur => ur.Role.UserRole == UserRole.ProjectMgr);
            //Проверяем список пользователей для назначения им роли ProjectMgr
            if (checkProjectMgr.Contains(user.UserID))
            {
                //Если в человека есть уже эта роль ProjectMgr, то не добавляем
                //Иначе добавляем эту роль
                if (isInRole)
                    continue;
                else
                {
                    user.UserRoles.Add(userRole);
                    db.UserRoles.Add(userRole);
                }
            }
            //Убираем роль ProjectMgr, у тех пользователей, которые не отмечены
            else
            {
                if (isInRole)
                {
                    user.UserRoles.Remove(userRole);
                    db.UserRoles.Remove(userRole);
                }
                else
                    continue;
            }
        }
        await db.SaveChangesAsync();
        return RedirectToAction("Index", "Admin");
    }

Детальный скрин ошибки:

Использую ASP.NET Core MVC v 2.2
В методе Index пробовал использовать AsNoTracking(), а в методе SetProjectMgr(int[] checkProjectMgr) для экземпляра userRole использовал: db.Entry(userRole).State = EntityState.Detached; и db.UserRoles.Attach(userRole); - результата никакого и проблема осталась

С чем это может быть связано?

Считаю, что это вопрос может быть полезен разработчикам работающих с EF и ASP.NET Core MVC

Спасибо за уделённое время и внимание.

Answer 1

Попробуй перед удалением выполнить

dbContext.ChangeTracker.Entries().ToList().ForEach(e => e.State = EntityState.Detached);
Answer 2

Проблема решена. Вопрос может быть закрыт. Всем спасибо за советы и участие.

Изменения коснулись метода SetProjectMgr
Этапы решения данной проблемы:

  1. Если задаём новую роль Role для пользователя User, то необходимо создать новый объект UserRole и добавить бд.
  2. При удалении роли UserRole.ProjectMgr, необходимо отыскать данную роль в пользователя userRole = user.UserRoles.SingleOrDefault(ur => ur.Role.UserRole == UserRole.ProjectMgr) и удалить её в db.UserRoles.Remove(userRole);.

В предыдущей (старой) версии метода SetProjectMgr создавался новый объект UserRole для каждого User, следовательно при добавлении всё работало отлично.
Но, при удалении UserRole выполнялась попытка найти и удалить несуществующий (новый созданный) объект в бд, что приводило к ошибке в строке db.UserRoles.Remove(userRole);.
Также был выполнен рефакторинг кода в данном методе.


Сам код метода приведён ниже:

    //Отметка людей как ProjectMgr
    [Authorize(Roles = "Admin, SuperAdmin")]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> SetProjectMgr(int[] checkProjectMgr)
    {
        //Присвоим роль пользователя при регистрации
        Role role = db.Roles.FirstOrDefault(r => r.UserRole == UserRole.ProjectMgr);
        MSIE.Data.ManyToManyEntities.UserRole userRole;
        foreach (User user in db.Users)//await db.Users.ToListAsync())
        {
            //Подгружаем роли пользователя
            db.Entry(user).Collection(u => u.UserRoles)
                .Query()
                .Include(ur => ur.Role)
                .Load();
            //Проверяем наличие роли ProjectMgr
            userRole = user.UserRoles.SingleOrDefault(ur => ur.Role.UserRole == UserRole.ProjectMgr);
            //Проверяем список пользователей для назначения им роли ProjectMgr
            if (checkProjectMgr.Contains(user.UserID))
            {
                //Если в человека есть уже эта роль ProjectMgr, то не добавляем
                //Иначе добавляем эту роль
                if(userRole != null)
                    continue;
                else
                {
                    //для роли пользователя
                    userRole = new Data.ManyToManyEntities.UserRole
                    {
                        Role = role,
                        User = user
                    };
                    //user.UserRoles.Add(userRole);
                    db.UserRoles.Add(userRole);
                }
            }
            //Убираем роль ProjectMgr, у тех пользователей, которые не отмечены
            else
            {
                if (userRole != null)
                {
                    //user.UserRoles.Remove(userRole);
                    db.UserRoles.Remove(userRole);
                }
                else
                    continue;
            }
        }
        await db.SaveChangesAsync();
        return RedirectToAction("Index", "Admin");
    }
READ ALSO
Unity. Создание кнопок с помощью скрипта

Unity. Создание кнопок с помощью скрипта

Работаю с движком Unity, на языке c#Я не знаю как сделать так, что бы кнопки созданные с помощью скрипта выполняли какой то метод, а именно присваивание...

224
Польза async await в asp.net и при ожидании Task.Delay

Польза async await в asp.net и при ожидании Task.Delay

У меня есть два вопросаЯ неплохо разобрался в асинхронном программировании в C#, но остались некоторые вопросы

190
сделать условие с выполнением AJAX

сделать условие с выполнением AJAX

Я новичок в ajax, хотелось бы спросить то, чего я в интернете не нашел

218