如果我有这样的物体
public class Movie
{
public long Id { get; set; }
public string Name { get; set; }
public long? Length { get; set; }
...
}
并有一个控制器来更新其值:
[HttpPatch("{id}")]
public IActionResult Patch(long id, [FromBody] Movie item)
{
Movie movie = (....
if (item.Length.HasValue)
{
movie.Length = item.Length;
_context.Entry<Movie>(movie).Property(w => w.Length).IsModified = true;
}
if (item.Name.HasValue)
{
movie.Name = item.Length;
_context.Entry<Movie>(movie).Property(w => w.Name).IsModified = true;
}
}
当提供了Length时,它可以工作,但是如果我想将其更新为undefined(空)怎么办? 如果在前端删除了该值,则PATCH数据为:
{"length":""}
在控制器上,模型绑定将其解释为null。
所以这个:
if (item.Length.HasValue)
使它什么也不做。
有一会儿我考虑删除if条件,因为如果传递了空字符串,则它将更新为null,但是,当然,如果修补了其他任何属性(例如Name),Length也将设置为null那就不好了 问题在于,控制器中没有方法(我可以看到)来确定Length为PATCHED但为空
答案 0 :(得分:1)
您可以尝试使用id
属性首先在数据库中获取某些电影项目,然后为该属性分配新值。
var movie = _context.Movies.SingleOrDefault(x => x.Id == id);
if (movie != null)
{
// if "item.length" is null, "movie.Length" would be null, too
// Otherwise, "movie.Length" will update new value based on "item.Length"
movie.Length = item.Length;
// or only if "item.Length" is null
if (item.Length == null)
{
movie.Length = null;
}
// update database synchronously
_context.SaveChanges();
// or update database asynchronously
// await _context.SaveChangesAsync();
}
if (movie != null)
{
if (!string.IsNullOrEmpty(item.Name) && item.Length?.HasValue)
{
movie.Name = item.Name;
movie.Length = item.Length;
}
else
{
movie.Name = string.Empty;
movie.Length = null;
}
// update database synchronously
_context.SaveChanges();
}
答案 1 :(得分:1)
您正面临经典的Nullable值类型问题。 Nullable对象的问题是如何确定该值是否真的为null并因此提供,而不是何时为Null简化(因为调用者认为这意味着“未修改”)被省略了。
在定义这类API或接口时,我认为它不仅仅只是提供一种与代码进行交互的方式。在这些情况下,我不是必须声明性的,而是可以要求解释的,而是选择命令性的,而是要求用户具体说明其意图。
因此,在这种情况下,我将添加一个可以设置为开或关的烦人的布尔成员,然后我可以将其读入,直到DTO看起来像
public class Movie
{
public long Id { get; set; }
public string Name { get; set; }
public long? Length { get; set; }
public Boolean IsLengthUpdated{get; set;} = false;//or something that makes sense to describe the length prop has been set
}
然后我可以探究我的模型,这样我就不必问自己太多关于他们的意图的问题了
因此,在您的补丁操作中,您可以做类似的事情
您的API调用者将IsLengthUpdated设置为true或将其保留为false,在这种情况下,您将忽略对Length属性的所有评估。这样,您的控制器将类似于
[HttpPatch("{id}")]
public IActionResult Patch(long id, [FromBody] Movie item)
{
Movie movie = (....
if(item.IsLengthUpdated)
{
movie.Length = item.Length;//here either movie.Length is supplied one of it's three states {Null,Updated to new value or left the same}
_context.Entry<Movie>(movie).Property(w => w.Length).IsModified = true;
}
... the rest of your code here
}
他们宁愿回来并说他们一直在更新值而不保留它们,所以我宁愿让他们打开相关的字段开关,否则他们可以保持原样,因为无论哪种方式,它始终默认为false。
答案 2 :(得分:0)
我认为这里不需要模型绑定,因为PATCH数据只是一个很小的json字符串,如下所示:
{"length":""}
所以我现在将其转换为不区分大小写的Dictionary,然后我仅使用发送的键和值:
[HttpPatch("{id}")]
public IActionResult Patch(long id, [FromBody] string patchstring)
{
JObject json = (JObject)JsonConvert.DeserializeObject(patchstring);
Dictionary<string, object> updatedict = new Dictionary<string, object>(json.ToObject<IDictionary<string, object>>(), StringComparer.CurrentCultureIgnoreCase);
Movie movie = (...);
if (updatedict.ContainsKey("Length"))
{
if ((string)updatedict["Length"] == "")
movie.Length = null;
else
movie.Length = Convert.ToInt64(updatedict["Length"]);
_context.Entry<Movie>(movie).Property(w => w.Length).IsModified = true;
}
if (updatedict.ContainsKey("Rating"))
{
if ((string)updatedict["Rating"] == "")
movie.Rating = null;
else
movie.Rating = Convert.ToInt64(updatedict["Rating"]);
_context.Entry<Movie>(movie).Property(w => w.Rating).IsModified = true;
}
_context.SaveChanges();
return new ObjectResult(movie);
}