C # — Entity Framework не обновляет объекты

#c# #entity-framework

#c# #entity-framework

Вопрос:

У меня есть простое приложение, которое выполняет стандартные операции CRUD.

Моя проблема в том, что на данный момент, похоже, он не редактирует значения в базе данных. Я отладил процесс и увидел, что он завершается сбоем в set.Attach(entity) строке в контексте.

Модель

 [Table("RepackRequest")]
public partial class RepackRequest
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public RepackRequest()
    {
    }

    public int ID { get; set; }

    [Required]
    public string FromItem { get; set; }

    [Required]
    public string ToItem { get; set; }

    public int Quantity { get; set; }

    public int? QuantityCompleted { get; set; }

    [Column(TypeName = "date")]
    public DateTime DateRequested { get; set; }

    [Required]
    public string RequestedBy { get; set; }

    [Required]
    public string RequestedByEmail { get; set; }

    public string ActionRequired { get; set; }

    [Required]
    public string Division { get; set; }

    [Required]
    [StringLength(1)]
    public string Status { get; set; }

    [Column(TypeName = "date")]
    public DateTime? CompletionDate { get; set; }

    [Required]
    public string Customer { get; set; }

    [Required]
    public string Priority { get; set; }

    [Required]
    public string EnteredBy { get; set; }

    public string CompletedBy { get; set; }

    public string FromLocation { get; set; }

    public string ToLocation { get; set; }

    public string ReworkedBy { get; set; }

    public string OriginalDriver { get; set; }

    public string FromItemDriver { get; set; }

    public string FromLocationDriver { get; set; }

    public string ToLocationDriver { get; set; }

    public string Workforce { get; set; }

    [Required]
    public string OrderNum { get; set; }

    [Column(TypeName = "date")]
    public DateTime PlannedCompletion { get; set; }

    [Column(TypeName = "date")]
    public DateTime? StartDate { get; set; }

}
  

Действие контроллера API

 [HttpPost, Route("api/Repack/Update")]
public async Task<HttpResponseMessage> UpdateRepack([FromBody] RepackRequest repack)
{
    var oldStatus = _uow.RepackService.Get(repack.ID).Status;
    _uow.RepackService.UpdateRepack(repack);
    _uow.Save();
    if (repack.Status == "C")
        await _helper.SendCompletedRepackEmail(repack);
    else if (repack.Status != oldStatus)
        await _helper.SendStatusChangeEmail(repack, oldStatus);
    return Request.CreateResponse(HttpStatusCode.OK, repack.ID);
}
  

Метод обслуживания

 public void UpdateRepack(RepackRequest repack)
{
    _context.SetModified(repack);
    _context.Save(); //_context.SaveChanges() called inside here
}
  

Контекстный метод

 public void SetModified<T>(T entity) where T : class
{
    var set = Set<T>();
    set.Attach(entity); //FAILS HERE
    Entry(entity).State = EntityState.Modified;
}
  

Я проверил, что идентификатор и т. Д. Объекта заполнены, Чтобы Entity Framework могла найти существующую запись, и теперь у меня нет идей.

Я вообще не получаю никаких сообщений об ошибках. Все, что я вижу, это то, что как только он пытается присоединить объект, он переходит к Dispose() методу моего UnityResolver.

Любая помощь была бы весьма признательна.

Спасибо!!!!

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

1. Каков тип исключения, сообщение и т.д. …?

2. Привет, извините, я пропустил эту информацию. Теперь я обновил свой вопрос, но ошибки или исключения не было. Он просто переходит к методу Dispose моего UnityResolver

3. вы пробовали _context.SaveChanges() ?

4. SaveChanges вызывается внутри _context.Save(); метод вызывается из метода службы UpdateRepack . Однако он не доходит до этой точки, поскольку ему не удается присоединить объект

5. Я прокомментировал строку кода, где она вызывается, просто для уточнения

Ответ №1:

Ошибка самоописывается. Причина в том, что вы получаете сущность из контекста, прежде чем присоединять ее к этой var oldStatus = _uow.RepackService.Get(repack.ID).Status; строке кода, а Entity Framework сохраняет ее в контексте. У вас есть два обходных пути:

Первый

В вашем UpdateRepack случае повторно извлеките объект из контекста, используя его идентификатор, и установите для значений новые значения.

 public void UpdateRepack(RepackRequest repack)
{
    RepackRequest fromDatabase = _uow.RepackService.Get(repack.ID);
    // Set current values to new values.
    _context.SetValues(fromDatabase, repack);
    _context.Save(); //_context.SaveChanges() called inside here
}

public void SetValues<T>(T entity, T currentEntity) where T : class
{
    var entry = Entry(entity);
    entry.CurrentValues.SetValues(currentEntity);
}
  

Не волнуйтесь, получение данных из контекста не будет дорогостоящей операцией, потому что они уже находятся в контексте. При использовании этого метода запрос на обновление будет отправлен для обновления только измененных свойств, тогда как если вы установите для состояния объекта значение изменено, то запрос на обновление будет отправлен для обновления всех значений столбцов.

Второй (не рекомендуется)

Вы можете использовать AsNoTracking , чтобы указать EntityFramework не сохранять полученную сущность в контексте. Но при этом каждый раз, когда вы пытаетесь получить запрос объекта, будет выполняться к базе данных. Кроме того, для обновления всех значений столбцов будет отправлен запрос на обновление, и это намного дороже, чем обновление только необходимых значений. Для этого вам следует добавить другой метод в RepackService вызываемую единицу работы GetAsNoTracking и реализовать его, как показано ниже:

 public Repack GetAsNoTracking(int id)
{
    return _context.Set<Repack>()
        .AsNoTracking()
        .First(m => m.ID == id);    
}
  

Затем вы должны использовать GetAsNoTracking , чтобы получить свой репак и не касаться оставшейся части вашего текущего кода. Поскольку он не хранится в привязке контекста, это не приведет к ошибке.

 [HttpPost, Route("api/Repack/Update")]
public async Task<HttpResponseMessage> UpdateRepack([FromBody] RepackRequest repack)
{
    var oldStatus = _uow.RepackService.GetAsNoTracking(repack.ID).Status;
    .........
}
  

Примечание: не рекомендуется сохранять данные для каждой операции. В шаблоне единицы работы вы должны фиксировать один раз вместо вызова SaveChanges метода для каждого действия. Из вашего кода я вижу, что у вас есть _uow.Save(); метод, и я полагаю, что вы вызываете _context.SaveChanges() метод внутри этого метода. Поступая таким образом, вам следует избегать вызова SaveChanges функций crud, таких как UpdateRepack method.

 public void UpdateRepack(RepackRequest repack)
{
    // Do needed operations
    // Remove this line. You are supposed to save changes in the end using _uow.Save(); -> _context.Save(); 
}
  

Ответ №2:

Вы пытаетесь присоединить объект, который не принадлежит контексту. вам нужно перенести этот объект по ключу, изменить выбранный объект, затем присоединить и сохранить (или просто сохранить его). Более подробная информация здесь

Ваш код должен быть чем-то вроде (как обсуждалось в комментариях):

 public void SetModified<T>(T entity) where T : class
{
    var set = Set<T>();
    var entityFromCtx = set.Where(x => x.Id == entity.Id).FirstOrDefault();
    Entry(entityFromCtx).CurrentValues.SetValues(entity);
    //set.Attach(entity); // you don't need this anymore since you're not disposing the context yet.
    Entry(entityFromCtx).State = EntityState.Modified;
    //set.SaveChanges(); // I don't know if this fits here, but just don't forget it.
}