简体   繁体   English

C#-实体框架不更新实体

[英]C# - Entity Framework not updating Entities

I have a simple app which performs standard CRUD operations. 我有一个简单的应用程序,可以执行标准的CRUD操作。

My issue is that at the moment, it does not seem to be editing values in the database. 我的问题是,目前看来,它似乎并不在编辑数据库中的值。 I have debugged through the process and seen that it fails on the set.Attach(entity) line in the context. 我已经调试了整个过程,发现它在上下文中的set.Attach(entity)行上失败。

Model 模型

[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 Controller Action 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);
}

Service Method 服务方式

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

Context Method 上下文方法

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

I have checked that the ID etc of the object is filled so that Entity Framework can find the existing record and im now out of ideas. 我已经检查了对象的ID等是否已填充,以便Entity Framework可以找到现有记录,并且现在不存在任何想法。

I dont get any error messages at all. 我根本没有收到任何错误消息。 All i see is that once it tries to attach the entity, it goes to the Dispose() method of my UnityResolver. 我所看到的是,一旦它尝试附加实体,它将转到我的UnityResolver的Dispose()方法。

Any help would be greatly appreciated. 任何帮助将不胜感激。

Thanks!!!! 谢谢!!!!

The error is self describing. 错误是自我描述。 The reason is that you get entity from context before attaching it by this var oldStatus = _uow.RepackService.Get(repack.ID).Status; 原因是在通过var oldStatus = _uow.RepackService.Get(repack.ID).Status;将其附加之前,您是从上下文中获取实体的var oldStatus = _uow.RepackService.Get(repack.ID).Status; code line and Entity Framework keeps it in context. 代码行和实体框架将其保留在上下文中。 You have two workarounds: 您有两种解决方法:

First 第一

In your UpdateRepack re-get the entity from context using its id and set the values to new values. 在您的UpdateRepack中,使用ID从上下文中重新获取实体,并将值设置为新值。

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);
}

Do not worry, getting data from context will not be costly operation, because it is already in the context. 不用担心,从上下文获取数据不会是昂贵的操作,因为它已经存在于上下文中。 By using this method update query will be sent to update only changed properties, whereas if you set state of entity to modified then update query will be sent to update all column values. 通过使用此方法,将发送更新查询以仅更新已更改的属性,而如果您将实体的状态设置为已修改,则将发送更新查询以更新所有列值。

Second (not recommended) 第二(不推荐)

You can use AsNoTracking to tell EntityFramework not to store received entity in the context. 您可以使用AsNoTracking告诉EntityFramework不要在上下文中存储接收到的实体。 But by doing so, every time you try to get object query will be executed against database. 但是这样做, 每次尝试获取对象查询都会针对数据库执行。 Additionally update query will be sent to update all column values and it much expensive than updating only needed values. 另外,将发送更新查询来更新所有列值,这比仅更新所需的值要昂贵得多。 To do so, you should add another method to RepackService of unit of work called GetAsNoTracking and implement it similar to below: 为此,您应该向工作单元的RepackService添加另一个方法,称为GetAsNoTracking并实现类似于以下内容的方法:

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

Then you should use GetAsNoTracking to get your repack and not touch to remaining part of your current code. 然后,您应该使用GetAsNoTracking获取重新打包的内容,而不要触摸当前代码的其余部分。 As it is not stored in context attaching it will not cause to an error. 由于它没有存储在上下文附加中,因此不会导致错误。

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

Note: It is not good practice to save data for every operation. 注意:为每个操作保存数据不是一个好习惯。 In Unit of Work pattern you are supposed to commit one time instead of calling SaveChanges method for every action. 工作单元模式中,您应该提交一次,而不是为每个操作调用SaveChanges方法。 From your code I see that you have _uow.Save(); 从您的代码中,我看到您有_uow.Save(); method and I believe that you call _context.SaveChanges() method inside this method. 方法,我相信您可以在此方法内调用_context.SaveChanges()方法。 By doing so, you should avoid calling SaveChanges at crud functions such as UpdateRepack method. 这样,您应该避免在诸如UpdateRepack方法之类的功能上调用SaveChanges

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(); 
}

You're trying to attach an entity that doesn't belong to the context. 您正在尝试附加一个不属于上下文的实体。 you need to bring that object by key, modify the fetched object then attach and save (or just save it). 您需要按键携带该对象,修改获取的对象,然后附加并保存(或保存)。 More details here 在这里更多细节

You're code should be something like (as discussed in the comments): 您的代码应类似于(如注释中所述):

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.
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM