PATCH, OrmLite partial updates, and null values

So given a simple model

public class Foo 
{
     public int Id {get; set;}
     public string A {get; set;}
     public string B {get; set;}
}

And a Patch DTO to Update the model

public class UpdateFoo : IReturn<Foo>
{
   public int Id {get; set;}
   public Foo foo {get; set;}
}

… Is there a clean way to clear out a particular field of a model? For instance, I can handle normal updates by doming something like:

public Foo Any(UpdateFoo request)
{
    var foo = Db.SingleById<Foo>(request.Id);
    foo.PopulateWithNonDefaultValues(request.Foo); 
    Db.Save(foo);
    return Db.SingleById<Foo>(request.Id);
}

This works fine for updates with new/changed data, but what if I want to flow a null, i.e. clear out a field? With the DTO the way I have it above, ServiceStack won’t be able to tell which field the client explicitly set to null. My searching online suggested changing my DTO to use a JSONPatch pattern, but I was wondering if there was a better way to do this.

OrmLite has a number of different Update* APIs if you only want to update a partial set of fields, e.g you can use UpdateOnly:

db.UpdateOnly(() => new Person { FirstName = "JJ" }, where: p => p.LastName == "Hendrix");

But you’ll need to specify all the fields you want to update in the lambda expression.

You can use UpdateNonDefaults if you just want to update properties which contains a value, e.g:

var model = request.ConvertTo<Person>();
db.UpdateNonDefaults(model, p => p.Id == request.Id);

Another way to filter out the properties to update is to specify the list of field names which contain values, e.g:

var model = request.ConvertTo<Person>();
var props = request.ToObjectDictionary();
var fieldNames = (from kvp in props where kvp.Value != null select kvp.Key).ToArray();
Db.UpdateOnly(model, onlyFields:fieldNames);

Example payload I would be dealing with:

{
     "id" : 123
     "foo": {
          "A": null
      }
}

UpdateNonDefaults doesn’t solve the problem of wanting to flow a null string, since null would be the default there. The last example seems to be the right approach, but I don’t think I can use the request object as is, request.Foo would include all the properties (not just the ones explicitly specified in the payload) after deserialization, right?

It’s not using the request object, it’s converted it to the data model in:

var model = request.ConvertTo<Person>();

This contains all the properties of the Request DTO:

var props = request.ToObjectDictionary();

This returns field names which have a value:

var fieldNames = (from kvp in props where kvp.Value != null select kvp.Key).ToArray();

Understood, but the problem is I want to update a field that has originally has a value, and set it to null, via a HTTP PATCH request.

so if Foo 123 is orignally

{
    "Id": 123,
    "A": "foo",
    "B": "bar"
}

and I want to set B to null via PATCH:

{
     "id" : 123
     "foo": {
          "B": null
      }
}

…and I modify your example to deal with the nested Foo object:

var model = request.Foo.ConvertTo<Foo>();
var props = request.Foo.ToObjectDictionary();

model would be an empty Foo object, with Id,A,B set to their default values, and props would be a three item dictionary object with null values. How do I tell the difference between what I have set explicitly in the request, and what is the default?

If I changed the Update DTO so that I didn’t nest the Foo object:

public class UpdateFoo : IReturn<Foo>
{
   public int Id {get; set;}
   public string A {get; set;}
   public string B {get; set;}
}

… not too much changes, model has Id = 123, but otherwise is empty. Props has all three fields, and I can’t tell which one I’m actually supposed to update.

Apologies if I’m not being clear, or if I’m being obtuse. It looks like in this scenario, the only way I could tell what to update is to look at the raw request body, but I might be missing something fundamental about how PATCH DTOs are supposed to work.

If you don’t want to update all fields then you’ll need a custom solution that inspects the Request DTO and call update APIs manually. ServiceStack or OrmLite doesn’t really have anything to help here.

Although I dislike nested complex types to capture this, it makes your API less interoperable and harder to use. If you need to capture which fields to set to NULL I’d just add a special Unset collection

public class UpdateFoo : IReturn<Foo>
{
    public int Id { get; set; }
    public string A { get; set; }
    public string B { get; set; }
    public string[] Unset  { get; set; }
}

Then your fieldNames can look like:

props.Remove(nameof(UpdateFoo.Unset));
var unset = (request.Unset ?? new string[0]).ToHashSet();
var fieldNames = (from kvp in props 
    where kvp.Value != null || unset.Contains(kvp.Key)
    select kvp.Key).ToArray();
1 Like

FYI I just added an UpdateOnly<T>(dictionary) API in this commit to make this a little easier which lets you update from an Object Dictionary, so you could do something like:

var props = request.ToObjectDictionary();
props.Remove(nameof(UpdateFoo.Id));
props.Remove(nameof(UpdateFoo.Unset));
request.Unset.Each(x => props[x] = null);
Db.UpdateOnly<Person>(props, where: x => x.Id == request.Id);

This change is available from v5.1.1 that’s now available on MyGet.

2 Likes

Thanks for the help and guidance as always!

The only other approach I was thinking was doing something like the opposite of what OrmLiteConfig.OnDbNullFilter does… check if my string is set to “NULL” and then update the model to null before saving it. If that setting were bidirectional, that would be ideal, but that’s easy enough to do as a custom solution if that’s not feasible…