Запрос Post завершается с ошибкой из-за того, что объект сообщает, что экземпляр объекта типа ‘WorkEntry’ не может быть отслежен из-за

#c# #entity-framework #post #entity

#c# #entity-framework #Публикация #объект

Вопрос:

Я использую базу данных InMemory из объекта, с которым у меня постоянно возникают проблемы. имея контроллер ниже, я продолжаю получать подобную ошибку The instance of entity type 'WorkEntry' 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. при выполнении post на маршруте id / workEntry.

 [Route("api/[controller]")]
public class ProjectsController : Controller
{
    private readonly ApiContext _context;

    public ProjectsController(ApiContext context)
    {
        _context = context;

    }

    [HttpGet]
    [Route("{Id:int}")]
    public string HelloWorld(int id)
    {
        return "Hello Back!";
    }


    // GET api/projects
    [HttpGet]
    public IActionResult Get() //async
    {
        return Ok(_context.Projects.Include(x => x.WorkEntries));
    }

    [HttpPost]
    public async Task<IActionResult> Post([FromBody] Project prj)
    {
        try
        {
            if (prj != null)
            {
                Console.WriteLine("Post--->  "   prj.Id   " "   prj.Name   " "   prj.StartDate   " "   prj.EndDate );
                _context.Projects.Add(prj);
                await _context.SaveChangesAsync();
                return Ok("Added new project with ID="  prj.Id);
            }
            return BadRequest("Project is null");
        }
        catch (Exception e)
        {
            return BadRequest(e.Message);
        }
    }

    //[HttpPut("{id}")]
    [HttpPost]
    [Route("{Id:int}/WorkEntry")]
    public async Task<IActionResult> PostAsync(int id, WorkEntry workEntry)
    {
        try
        {
            if (workEntry != null)
            {
                Console.WriteLine("asta e WorkEntryyyyy---- "   workEntry);
                var updatedProject = _context.Projects.Include(x => x.WorkEntries).First(x => x.Id == id);
                Console.WriteLine(updatedProject.ToString());

                updatedProject.WorkEntries.Add(workEntry);
                _context.Projects.Attach(updatedProject);
                await _context.SaveChangesAsync();
                //db.Books.AddOrUpdate(book)
                return Ok("project with ID="   updatedProject.Id   " was updated");

            }
            return BadRequest("Project or WorkEntities is null");///schimba

        }
        catch (Exception e)
        {
            return BadRequest(e.Message);
        }
    }

}
  

Кто-нибудь знает, что я делаю не так? Я пробовал разные подходы, в которых я использовал AsNoTracking или использовал update, но ничего мне не помогло.
Любой намек хорош для меня 🙂

Спасибо

Ответ №1:

Вам не нужно использовать _context.Projects.Attach(updatedProject); , потому что по умолчанию EntityFramework записи отслеживания при чтении из базы данных и при присоединении объекта к EntityFramework отслеживаемому Wntitifeamrwork произошла ошибка

Экземпляр объекта типа ‘WorkEntry’ не может быть отслежен, поскольку другой экземпляр с тем же значением ключа для {‘id’} уже отслеживается

Вы должны удалить _context.Projects.Attach(updatedProject); или использовать .AsNoTracking() для исправления этой ошибки.

  1. Удалить .Attach(updatedProject);
 var updatedProject = _context.Projects.Include(x => x.WorkEntries).First(x => x.Id == id);
Console.WriteLine(updatedProject.ToString());
updatedProject.WorkEntries.Add(workEntry);
//_context.Projects.Attach(updatedProject);
await _context.SaveChangesAsync();
  
  1. использование .AsNoTracking()
 var updatedProject = _context.Projects.AsNoTracking().Include(x => x.WorkEntries).First(x => x.Id == id);
Console.WriteLine(updatedProject.ToString());
updatedProject.WorkEntries.Add(workEntry);
_context.Projects.Attach(updatedProject);
await _context.SaveChangesAsync();
  

Комментарии:

1. То, что вы мне здесь сказали, мне очень помогло, но дело в том, что моя БД не сохраняет никаких изменений вообще. все остальное работает сейчас, но ничего не сохраняется.

2. нужен ли _context.Projects . Обновление (updatedProject); может быть? но если я использую это, то я возвращаюсь к этой системе ошибок. Исключение InvalidOperationException: экземпляр объекта типа ‘WorkEntry’ не может быть отслежен, поскольку другой экземпляр с тем же значением ключа для {‘id’} уже отслеживается. При присоединении существующих объектов убедитесь, что присоединен только один экземпляр объекта с заданным значением ключа. Рассмотрите возможность использования ‘DbContextOptionsBuilder. enablesensitivedatallogging’ для просмотра конфликтующих значений ключа.

3. @40love Используете ли вы одно из моих решений? вы не должны использовать оба решения. вы можете использовать один из них

Ответ №2:

Ответ Фархада должен охватывать наиболее вероятные проблемы. Еще одна вещь, которую нужно проверить, это то, что похоже, что вы используете внедрение зависимостей, поэтому убедитесь, что ваш DbContext настроен с пожизненной областью для каждого запроса, а не что-то вроде Singleton.

Другое дело, что неясно, пытаетесь ли вы добавить WorkEntry или обновить его. Код, который у вас есть (минус Attach ), должен работать для сценария добавления. Если вы хотите обновить, вам следует скопировать значения из вашей переданной модели в загруженный и отслеживаемый объект. Automapper удобен для помощи здесь:

 var updatedProject = _context.Projects.Include(x => x.WorkEntries).Single(x => x.Id == id);

var existingWorkEntry = updatedProject.WorkEntries.SingleOrDefault(x => x.Id == workEntry.Id);
if (existingWorkEntry != null)
{
    var config = new MapperConfiguration(cfg => cfg.CreateMap<WorkEntry, WorkEntry>());
    var mapper = config.CreateMapper();
    mapper.Map(existingWorkEntry, workEntry);
}
else // Include this if the method could receive a new Work Entry...
    updatedProject.WorkEntries.Add(workEntry);

await _context.SaveChangesAsync();
  

Обязательно тщательно проверяйте любые / все данные, поступающие от клиента при вызове POST, поскольку они могут быть изменены с помощью инструментов отладки и тому подобного. Передача объектов между клиентом и сервером и их привязка к измененному состоянию уязвима для подделки и очень неэффективна с точки зрения использования памяти и размера полезной нагрузки передачи. Этот же метод с помощью Automapper может быть применен к обновлению объектов с использованием более упрощенных объектов ViewModel / DTO, уменьшая количество данных, передаваемых по проводу, до того, что представление должно передавать, а не целых объектов.