【问题标题】:.NET API Update Includes ID.NET API 更新包括 ID
【发布时间】:2015-04-09 17:30:45
【问题描述】:

我来自 Ruby on Rails API 背景,但目前正在研究 .NET C# WebAPI。我熟悉 C# 和 .NET 网络表单。

我正在尝试设置将更新数据库中的记录的 PUT 请求。脚手架方法会覆盖所有字段,而我只想更新通过 PUT 传递的字段。我尝试使用以下代码:

// PUT: api/Users/5
        [ResponseType(typeof(void))]
        public IHttpActionResult PutUser(string id, User user)
        {

            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            User userToUpdate = db.Users.Where(u => u.id == id).FirstOrDefault();
            if (userToUpdate == null)
            {
                return NotFound();
            }

            db.Entry(userToUpdate).CurrentValues.SetValues(user);

            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateException)
            {
                if (UserExists(id))
                {
                    return Conflict();
                }
                else
                {
                    throw;
                }
            }

            return StatusCode(HttpStatusCode.NoContent);
        }

但是,该方法在 db.SaveChanges() 上失败,因为它无法覆盖“id”,这是一个键。 put请求是content-type: application/json,json如下:

{
    "preferredEmailUpdates":"true"
}

这需要适用于所有字段并接受空条目。所以下面的内容也是有效的,应该用 null 更新字段电话。

{
    "preferredEmailUpdates":"true",
    "phone":null
}

如何在不更新密钥的情况下更新这些值?

【问题讨论】:

  • 如果您不想设置所有值,则不能使用 CurrentValues.SetValues(),您必须单独复制每个字段。

标签: c# .net json api updates


【解决方案1】:

您可以考虑使用PATCH HTTP 动词和Delta<T> 对象。

    [AcceptVerbs("Patch"), ResponseType(typeof(void))]
    public IHttpActionResult PatchUser(string id, Delta<Team> changes)
    {
        User userToUpdate = db.Users.Where(u => u.id == id).FirstOrDefault();
        if (userToUpdate == null) 
           return NotFound();

        changes.Patch(userToUpdate);

        try
        {                
            db.SaveChanges()
        }
        catch (DbUpdateException)
        {
           ...
        }

        return StatusCode(HttpStatusCode.NoContent);
    }

另请参阅Easy ASP.NET Web API resource updates with Delta

【讨论】:

  • 请注意,Delta 仅适用于 OData 格式的数据,不适用于 .NET Json。尽管它确实适用于某些数据类型,但诸如 Int32 之类的类型无法按预期运行。但是,如果您使用此处使用的补丁(不是双关语),则可以解决此问题:aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/…
  • 为什么要在此处重新打开重复并回答,将知识分散到多个问题上,而不是将您的答案添加到重复的stackoverflow.com/questions/16137766/…
【解决方案2】:

您的更新模型(用户请求绑定到的类)应该与您的数据库模型分开(因为它代表不同的概念)。它将具有可空/可能的字段来表示可能的更改。它还会隐藏您不希望用户更改的字段(例如时间戳)。然后,您可以使用反射仅更新已修改的字段。考虑使用 AutoMapper 等即用型库

using System;
using System.Reflection;
using WhateverNamespaceIsNeededForWebApiAndEF;

public class UserUpdate
{
  public String Name {get; set;}
  public bool? PreferredEmailUpdates {get; set;}
}
public class User
{
  public int Id {get; set;}
  public String Name {get; set;}
  public bool PreferredEmailUpdates {get; set;}
  public DateTimeOffset CreationDate {get; set;}
}

[ResponseType(typeof(void))]
public IHttpActionResult PutUser(string id, UserUpdate command)
{

    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    User userToUpdate = db.Users.Where(u => u.id == id).FirstOrDefault();
    if (userToUpdate == null)
    {
        return NotFound();
    }

    // THIS IS THE IMPORTANT CODE
    PropertyInfo[] propInfos = command.GetType().GetProperties(BindingFlags.Public|BindingFlags.Instance);
    foreach(var p in propInfos)
    {
        object value = p.GetValue(command);
        if(value != null)
        {
            userToUpdate.GetType().GetProperty(p.Name).SetValue(userToUpdate, value);
        }
    }
    // END IMPORTANT CODE

    try
    {
        db.SaveChanges();
    }
    catch (DbUpdateException)
    {
        if (UserExists(id))
        {
            return Conflict();
        }
        else
        {
            throw;
        }
    }

    return StatusCode(HttpStatusCode.NoContent);
}

另一种情况是 - 为什么要更新单个字段?能够在不更新密码的情况下更新电子邮件首选项是正常的,但是单独更新所述首选项的每个字段是否正常?我对此表示怀疑。考虑将 API 拆分为块,就像在 OOP 中拆分大接口一样。

【讨论】:

  • 这看起来是一个好的开始,但是我想取消像 {"email":"testy.com", "phone":null} 这样的值的情况再次无法正确更新,因为电话将被跳过。
  • 您应该考虑使用 Maybe 来表示修改的可能性。 AFAIK .NET 中没有合适的类,但构建起来很简单。 This post 好像在解释这个想法。
猜你喜欢
  • 2021-10-29
  • 2013-11-17
  • 1970-01-01
  • 2021-12-19
  • 1970-01-01
  • 2018-05-06
  • 1970-01-01
  • 1970-01-01
  • 2018-11-07
相关资源
最近更新 更多