1
\$\begingroup\$

I'm working on an implementation of the HTTP PATCH method for WebAPI. I know there are packages like JsonPatch and OData Delta for that, but they don’t work for me. JsonPatch is too complicated and OData Delta can't work with Int32 an Guid.

When I send PATCH to the API I need mapping to model the class only, those properties that are in JSON.

Let's say I have model Person:

class Person
{
   public int Id { get; set; }
   public string Name { get; set; }
   public int? Age { get; set; }
}

The I send JSON using HTTP PATCH:

{
   "Name": "new,
   "Age":null
}

So, person should be updated: name set to "new" and age set to null. For that I created a Patch class:

public sealed class Patch<TModel> : DynamicObject where TModel : class {
    private readonly IDictionary<PropertyInfo, object> _changedProperties = new Dictionary<PropertyInfo, object>();
    public override bool TrySetMember(SetMemberBinder binder, object value) {
        var propertyInfo = typeof(TModel).GetProperty(binder.Name);
        if (propertyInfo != null) {
            _changedProperties.Add(propertyInfo, value);
        }
        return base.TrySetMember(binder, value);
    }

    public void Apply(TModel delta) {
        foreach (var property in _changedProperties) {
            object value = ChangeType(property.Value, property.Key.PropertyType);
            property.Key.SetValue(delta, value);
        }
    }

    private static object ChangeType(object value, Type type) {
        try {
            if (type == typeof(Guid)) {
                return Guid.Parse((string)value);
            }
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
                if (value == null) {
                    return null;
                }
                type = Nullable.GetUnderlyingType(type);
            }
            return Convert.ChangeType(value, type);
        } catch {
            return null;
        }
    }
}

And use it like:

[HttpPatch]
public IEnumerable<string> Patch([FromBody] Patch<Person> person) {
    person.Apply(_person);

I need some sugestions on how it can be improved and made safer. I do not have any idea about what kind of refactoring I can use here. How can I handle, for example, IEnumerable properties?

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.