Asp.net core 如何使用模型绑定将值修补为null

Asp.net core 如何使用模型绑定将值修补为null,asp.net-core,.net-core,Asp.net Core,.net Core,如果我有这样一个物体 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

如果我有这样一个物体

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;
        }
    }
在控制器上,模型绑定将其解释为null

因此:

if (item.Length.HasValue)
让它什么也不做

有一段时间,我考虑删除if条件,因为如果传递了空字符串,它会将其更新为null,但当然,如果对Name等任何其他属性进行了修补,那么长度也会设置为null,这将是不好的。
问题是控制器中没有办法(我可以看到)确定长度已修补,但为空

您可以尝试使用
id
属性首先在数据库中获取一些电影项目,然后将新值分配给属性

var movie=\u context.Movies.SingleOrDefault(x=>x.Id==Id);
如果(电影!=null)
{
//如果“item.length”为空,“movie.length”也将为空
//否则,“movie.Length”将根据“item.Length”更新新值
movie.Length=item.Length;
//或者仅当“item.Length”为空时
如果(item.Length==null)
{
movie.Length=null;
}
//同步更新数据库
_SaveChanges();
//或者异步更新数据库
//wait_context.SaveChangesAsync();
}

if(电影!=null)
{
if(!string.IsNullOrEmpty(item.Name)和&item.Length?.HasValue)
{
movie.Name=item.Name;
movie.Length=item.Length;
}
其他的
{
movie.Name=string.Empty;
movie.Length=null;
}
//同步更新数据库
_SaveChanges();
}

您可以尝试使用
id
属性首先获取数据库中的某些电影项目,然后为属性指定新值

var movie=\u context.Movies.SingleOrDefault(x=>x.Id==Id);
如果(电影!=null)
{
//如果“item.length”为空,“movie.length”也将为空
//否则,“movie.Length”将根据“item.Length”更新新值
movie.Length=item.Length;
//或者仅当“item.Length”为空时
如果(item.Length==null)
{
movie.Length=null;
}
//同步更新数据库
_SaveChanges();
//或者异步更新数据库
//wait_context.SaveChangesAsync();
}

if(电影!=null)
{
if(!string.IsNullOrEmpty(item.Name)和&item.Length?.HasValue)
{
movie.Name=item.Name;
movie.Length=item.Length;
}
其他的
{
movie.Name=string.Empty;
movie.Length=null;
}
//同步更新数据库
_SaveChanges();
}

您面临一个经典的可空值类型问题。可为空对象的问题是,如何确定值是否真的打算为空并因此提供,而不是当值为空时,因为调用者认为这意味着“未修改”,所以省略了它

在定义这些类型的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
    }
[HttpPatch(“{id}”)]
公共IActionResult修补程序(长id,[FromBody]电影项目)
{
电影。。。。
如果(项目已更新)
{
movie.Length=item.Length;//此处提供了movie.Length的三种状态之一{Null、更新为新值或保持不变}
_context.Entry(movie.Property(w=>w.Length).IsModified=true;
}
…这里是您的其余代码
}

他们宁愿回来说他们一直在更新值而没有被持久化,那么我宁愿让他们打开相关的字段开关,否则他们可以保持原样,因为无论哪种方式,它都会默认为false。

您面临一个经典的可空值类型问题。可空值对象的问题是怎么做您确定该值是否真的打算为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
    }
[HttpPatch(“{id}”)]
公共IActionResult修补程序(长id,[FromBody]电影项目)
{
电影。。。。
如果(项目已更新)
{
movie.Length=item.Length;//此处提供了movie.Length的三种状态之一{Null、更新为新值或保留
[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); 
}