0

Yesterday I came across a problem. I was asked to write a rest service which exposes all properties of a class. The consumers would all consume different properties and when submitting they would all send different subsets of them. For example sake lets call the contract company.

class Company{
    public string Address {get;set;}
    public string CompanyNumber {get;set;}
    public string Turnover {get;set;}
    public string Employees {get;set;}
}

Lets say we have two known systems which would like to sync the Company class. System 1 deals with accounting and wants to read Address and Update Turnover. System 2 deals with hr and wants to read Address and update Employees. Now normally when faced with this problem one would write lots of interfaces each with a tailored contract to suit the end system. However I have been told this is not what they want. Instead if the property is supplied in the JSON it must be set.

The problem is when deserialising the class Company from JSON if the property is not supplied the property is null, this will then when mapped to the database classes overwrite the data.

Mark
  • 1,544
  • 1
  • 14
  • 26
  • related to http://stackoverflow.com/questions/22596472/json-net-partial-update-rest-api-client?rq=1 – Mark Mar 09 '16 at 13:11

1 Answers1

0

The solution I came up with was to create an Optional struct like Nullable but where null can also be a valid value. When deserialising this struct can track if the property was set, and therefore provided in the JSON. I can then check this when mapping from the DTO to the database class and map only when the property is set.:)

https://dotnetfiddle.net/QmHtSW

public interface IOptional
{
    bool HasBeenSet { get; }
}
public struct Optional<T> : IOptional
{

    internal readonly T Val;

    public Optional(T value)
    {
        Val = value;
        HasBeenSet = true;
    }

    public Optional(Optional<T> value)
    {
        Val = value.Value;
        HasBeenSet = value.HasBeenSet;
    }

    public bool HasBeenSet { get; }

    public override bool Equals(object obj)
    {
        return obj.Equals(Val);
    }
    public override int GetHashCode()
    {
        return Val.GetHashCode();
    }

    public static implicit operator T(Optional<T> optional)
    {
        return optional.Val;
    }

    public static implicit operator Optional<T>(T optional)
    {
        return new Optional<T>(optional);
    }

    public T Value => Val;
}

and my AutoMapper mapping profile

 CreateMap<TRestObject, TDomainObject>()
            .ForAllMembers(mo=>mo.Condition(f =>
            {
                var opt = f.SourceValue as IOptional;
                return opt==null || opt.HasBeenSet;
            }));
Mark
  • 1,544
  • 1
  • 14
  • 26
  • made it into a nuget package with a host of support for other bits https://github.com/mgazza/Optional https://www.nuget.org/packages/Mgazza.Optional/ – Mark Mar 31 '16 at 07:54