C# Web API 2和EF附加问题

C# Web API 2和EF附加问题,c#,entity-framework-6,asp.net-web-api2,C#,Entity Framework 6,Asp.net Web Api2,我有一个使用WebAPI2开发的web服务,它使用EF6将数据保存回数据库 我的数据结构如下 public class User { [Key] public int UserId { get; set; } public string FullName { get; set; } public string Email { get; set; } } public class Contact { [Key] public int Contact

我有一个使用WebAPI2开发的web服务,它使用EF6将数据保存回数据库

我的数据结构如下

public class User
{
    [Key]
    public int UserId { get; set; }
    public string FullName { get; set; }
    public string Email { get; set; }
}

public class Contact
{
    [Key]
    public int ContactId { get; set; }

    public string ContactName { get; set; }

    public int CreatedById { get; set; }
    [ForeignKey("CreatedById")]
    public User CreatedBy { get; set; }

    public int ModifiedById { get; set; }
    [ForeignKey("ModifiedById")]
    public User ModifiedBy { get; set; }
}

public class Note
{
    [Key]
    public int NoteId { get; set; }

    public string Notes { get; set; }

    public int ContactId { get; set; }
    [ForeignKey("ContactId")]
    public Contact Contact { get; set; }

    public int CreatedById { get; set; }
    [ForeignKey("CreatedById")]
    public User CreatedBy { get; set; }

    public int ModifiedById { get; set; }
    [ForeignKey("ModifiedById")]
    public User ModifiedBy { get; set; }
}
我使用boiler plate代码尝试保存对WebAPI中注释的修改,如下所示

    // PUT: api/Notes/5
    [ResponseType(typeof(void))]
    public IHttpActionResult PutNote(int id, Note note)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != note.NoteId)
        {
            return BadRequest();
        }

        db.Entry(note).State = EntityState.Modified;

        try
        {
            db.SaveChanges();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!AttachmentExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return StatusCode(HttpStatusCode.NoContent);
    }
然而,当修改entitystate的行被执行时,我得到以下异常

附加“User”类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“Unchanged”或“Modified”时可能会发生这种情况。这可能是因为某些实体是新的,尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“添加”实体状态跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”

我发现这令人费解,因为我没有手动附加任何实体,而且db.ChangeTracker.Entries()此时是空的。我原以为EF可以处理同一个实体可以在树中多次引用的事实

有没有人遇到过这个问题,有没有人能解决这个问题

多谢各位


Neil.

我认为部分问题在于,您的笔记模型可能从未正确加载到EF的对象图中进行跟踪

请尝试以下伪(ish)代码:


我认为问题的一部分是,您的笔记模型可能从未正确加载到EF的对象图中进行跟踪

请尝试以下伪(ish)代码:


您甚至没有对
note
参数执行任何操作。如果您正在发出
PUT
请求,请至少修改一个属性。修改将在客户端进行,并发送到web api请求,因此修改将包含在note参数中。我认为Amit为您指明了正确的方向。请参阅下面我的答案。您甚至没有对
note
参数执行任何操作。如果您正在发出
PUT
请求,请至少修改一个属性。修改将在客户端进行,并发送到web api请求,因此,修改已经包含在note参数中。我认为Amit为您指明了正确的方向。请看下面我的回答。感谢您的回复,我可以看到您所说的是如何工作的,但是,如果我逐字应用您的示例,这意味着我的控制器必须为对象上的每个属性都有一个语句来更新它,这在维护和修改方面不是特别友好。如果一个类在以后更新,那么很可能会忘记控制器。您在建议automapper时已经解决了这一问题,但我发现,当使用automapper复制属性时,所有相关对象都附加了一个Added状态,这显然是不正确的。您的代码混乱是正确的。这只是一个例子。您可以通过将属性映射提取到它自己的类来重构它。然后对控制器中的转换器/映射器进行单线调用。理想情况下,这应该通过依赖注入来完成。但是如果你不熟悉这个原理,不用担心。这个问题超出了范围。我现在正在打电话,但我很乐意稍后发布一些代码示例。如果可以的话,那太好了,非常感谢您的帮助。@PercivalMcGullicuddy您能发送代码示例吗?谢谢您的回复,我可以看到您所说的是如何工作的,如果我逐字应用您的示例,这意味着我的控制器必须为对象上的每个属性都有一个语句来更新它,这在维护和修改方面不是特别友好。如果一个类在以后更新,那么很可能会忘记控制器。您在建议automapper时已经解决了这一问题,但我发现,当使用automapper复制属性时,所有相关对象都附加了一个Added状态,这显然是不正确的。您的代码混乱是正确的。这只是一个例子。您可以通过将属性映射提取到它自己的类来重构它。然后对控制器中的转换器/映射器进行单线调用。理想情况下,这应该通过依赖注入来完成。但是如果你不熟悉这个原理,不用担心。这个问题超出了范围。我现在正在打电话,但我很乐意稍后发布一些代码示例。如果可以的话,那太好了,非常感谢您的帮助。@PercivalMcGullicuddy您能发送代码示例吗?
// PUT: api/Notes/5
[ResponseType(typeof(void))]
public IHttpActionResult PutNote(int id, Note note)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    if (id != note.NoteId)
        return BadRequest();

    //Not sure how you're setup, but have EF fetch the note you want
    //to modify from the DB. It is now aware of it and tracking changes.
    var model = _getNoteModelFromDbById(note.NoteId);

    //Now that you have the model from the DB you can map the properties 
    //of the incoming note to your model. Bellow is just a basic example.         
    //I recommend you look into a library called Automapper later on. 

     model.Title = note.Title;
     model.Description = note.Description;
     model.Status = note.Status;

    db.Entry(model).State = EntityState.Modified;

    try
    {
        db.SaveChanges();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!AttachmentExists(id))
            return NotFound();
        else
            throw;
    }
    return StatusCode(HttpStatusCode.NoContent);
}