.NET API 更新包括 ID
.NET API Update Includes ID
我来自 Ruby Rails API 背景,但目前正在研究 .NET C# WebAPI。我熟悉 C# 和 .NET webforms。
我正在尝试设置将更新数据库中记录的 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.
更新字段 phone
{
"preferredEmailUpdates":"true",
"phone":null
}
如何在不更新密钥的情况下更新这些值?
您的更新模型(class 用户请求绑定到)应该与您的数据库模型分开(因为它代表不同的概念)。它会有 nullable / maybe 字段来表示可能的更改。它还会隐藏您不希望用户更改的字段(如时间戳)。然后,您可以使用反射仅更新已修改的字段。考虑使用现成的库,如 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 中拆分大接口一样。
您可以考虑使用 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);
}
我来自 Ruby Rails API 背景,但目前正在研究 .NET C# WebAPI。我熟悉 C# 和 .NET webforms。
我正在尝试设置将更新数据库中记录的 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.
更新字段 phone{
"preferredEmailUpdates":"true",
"phone":null
}
如何在不更新密钥的情况下更新这些值?
您的更新模型(class 用户请求绑定到)应该与您的数据库模型分开(因为它代表不同的概念)。它会有 nullable / maybe 字段来表示可能的更改。它还会隐藏您不希望用户更改的字段(如时间戳)。然后,您可以使用反射仅更新已修改的字段。考虑使用现成的库,如 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 中拆分大接口一样。
您可以考虑使用 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);
}