如何使用导航属性正确保存对实体的更改

时间:2016-03-14 21:04:48

标签: c# entity-framework asp.net-web-api

我正在尝试编写PUT-action的正确实现。我有一个基本的Model类(让我们称之为Model)。

public class Model
{
    [Key]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public User Creator { get; set; }
}

Creator是必需的导航属性,它通过外键引用User实体。 我的模型也有一个简单的DTO类,它是从客户端发布的。

public class ModelDTO
{
    public int Id { get; set; }
    public string Name { get; set; }
}

正如您所看到的,DTO类不包含Creator引用,因为我们不需要从客户端更新它。

这是PUT动作。它基于Visual Studio为我设计的模板。我刚刚添加了Automapper调用来将我的DTO映射到Model。

    // PUT: api/Models/5
    [ResponseType(typeof(void))]
    public async Task<IHttpActionResult> PutModel(int id, ModelDTO modelDTO)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != Model.Id)
        {
            return BadRequest();
        }

        var model = Mapper.Map<Model>(modelDTO); // i've just added this
        db.Entry(model).State = EntityState.Modified;

        try
        {
            await db.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!ModelExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return StatusCode(HttpStatusCode.NoContent);
    }

问题是SaveChangesAsync方法因Db验证错误而崩溃'创建者字段是必需的'。 从技术上讲,我理解Creator是null因为我已经创建了新的Model实例,EF不知道我是否要将Creator设置为null或忽略它。我需要以某种方式声明我不想设置创建者。我只想更新名称(或其他未来的标量属性)。那么如何在不加载(包括)导航属性数据的情况下正确地完成它? 是否有一些常见的最佳实践方法?

感谢。

2 个答案:

答案 0 :(得分:0)

好的,一旦你抛出脚手架为你生成的垃圾,也许你可以尝试编写一些有用的代码并增加你有一些工作解决方案的机会。

您可以从DTO / viewmodel =&gt;中删除Id属性开始这来自其他地方(路线段),所以它不应该是您的更新视图模型的一部分:

public class ModelDTO
{
    // The name is required => make sure that your DTO
    // respects this requirement as well before even
    // reaching your backend
    [Required]
    public string Name { get; set; }
}

很酷,现在你可以继续编写PUT控制器动作的代码了:

[HttpPut]
public async Task<IHttpActionResult> Put(int id, ModelDTO modelDTO)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    // Get the model that you are trying to update from the DB
    // using the id:
    var model = db.Models.Find(id);

    if (model == null)
    {
        // the id that was provided as parameter doesn't exist in the DB =>
        // we don't need to go any further, just let the client know
        // by returning 404
        return NotFound();
    }

    // set the properties that you want to update (only Name in your case)
    // of course when you have more complex models you might consider using
    // AutoMapper to take care of this part, but for the purposes of this
    // demonstration you don't need it:
    model.Name = modelDTO.Name; 

    // Now save the changes back to your database
    await db.SaveChangesAsync();

    return StatusCode(HttpStatusCode.NoContent);
}

好的,所以这里要记住的事情如下:

  

当某个工具试图在他的位置编写代码时,开发人员应该做的第一件事就是简单地抛弃这个工具/代码并开始完成他的工作(在开发人员的情况下就是编写代码)。

答案 1 :(得分:0)

  

我只想更新名称(或其他未来的标量属性)。那么如何在不加载(包括)导航属性数据的情况下正确地完成它?是否有一些常见的最佳实践方法?

因此您希望更新某些特定字段,并在您的案例中Name字段

 //// find your model 
 var entity = db.Models.Find(id);
 entity.Name=modelDTO.Name; 
 /// set modified property
 db.Entry(entity).Property(c => c.Name).IsModified = true     
 await db.SaveChangesAsync();