#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()
для исправления этой ошибки.
- Удалить
.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();
- использование
.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, уменьшая количество данных, передаваемых по проводу, до того, что представление должно передавать, а не целых объектов.