Как ПАТЧИРОВАТЬ значение, чтобы быть нулевым с привязкой модели - PullRequest
0 голосов
/ 24 ноября 2018

Если у меня есть такой объект

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;
        }
    }

Он работает, когда указана длина, но что, если я хочуобновить его, чтобы быть неопределенным (ноль)?Если значение удалено во внешнем интерфейсе, данные PATCH:

{"length":""}

В контроллере привязка модели интерпретирует это как нуль.

Так вот:

if (item.Length.HasValue)

заставляет его ничего не делать.

На мгновение я подумал об удалении условия if, потому что тогда оно обновило бы его до нуля, если пустая строка была передана, но, конечно, если любое другое свойство, такоепоскольку имя исправлено, длина также будет установлена ​​на ноль, что было бы плохо.Проблема в том, что в контроллере нет способа (который я вижу) определить, что длина была исправлена, но равна нулю

Ответы [ 3 ]

0 голосов
/ 24 ноября 2018

Вы столкнулись с классической проблемой типов значений Nullable.Проблема с объектами Nullable состоит в том, как определить, действительно ли значение предназначено для того, чтобы быть нулевым и, следовательно, предоставленным, по сравнению с тем, когда значение 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
}

Тогда я смогуисследовать мою модель как таковую, чтобы мне не приходилось задавать себе слишком много вопросов об их намерениях

Так что в своем действии Patch вы можете просто сделать что-то вроде

Ваш вызывающий API должен установить либоIsLengthUpdated to 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.

0 голосов
/ 25 ноября 2018

Я решил, что привязка модели здесь не то, что мне нужно, потому что данные PATCH - это только крошечная строка json, подобная этой:

{"length":""}

Так что теперь я преобразовываю ее в словарь без учета регистра, итогда я работаю только с ключом и значением, которое было отправлено:

[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); 
}
0 голосов
/ 24 ноября 2018

Вы можете попытаться использовать свойство 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...