json-api-dotnet/JsonApiDotNetCore

Explore usage of INotifyPropertyChanged with NSwag clients

bkoelman opened this issue · 2 comments

If possible, this smoothens the experience of partial post/patch for atomic operations. Otherwise, the existing extension method must take the operation index as a parameter.

This would be great to use for partial patch, where sending "firstname": null means something different than omitting it.

Unfortunately, this isn't currently possible, because the client code generated by NSwag performs a comparison before raising the INotifyPropertyChanged.PropertyChanged event. For example:

<PropertyGroup>
  <NSwagClassStyle>INPC</NSwagClassStyle>
</PropertyGroup>

Generates the following code:

[Newtonsoft.Json.JsonProperty("firstName", Required = Newtonsoft.Json.Required.Default,
    NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string? FirstName
{
    get { return _firstName; }
    set
    {
        if (_firstName != value) // <----- this check must be removed, so we can detect null/default assignments
        {
            _firstName = value;
            RaisePropertyChanged();
        }
    }
}

As a workaround, we'd need to ship a custom template for NJsonSchema, based on the default template.

If we did, the following would work:

var updatePersonRequest = new UpdatePersonRequestDocument
{
    Data = new DataInUpdatePersonRequest
    {
        Id = "1",
        // This line results in sending "firstName: null" instead of omitting it.
        Attributes = new TrackChangesFor<AttributesInUpdatePersonRequest>(_apiClient)
        {
            Initializer =
            {
                FirstName = null,
                LastName = "Knight Rider"
            }
        }.Initializer
    }
};

var response = await ApiResponse.TranslateAsync(async () =>
    await _apiClient.PatchPersonAsync(updatePersonRequest.Data.Id, updatePersonRequest));

Likewise, it can now be used in an atomic:operations request:

var operationsRequest = new OperationsRequestDocument
{
    Atomic_operations =
    [
        new CreatePersonOperation
        {
            Data = new DataInCreatePersonRequest
            {
                Lid = "new-person",
                // This line results in sending "firstName: null" instead of omitting it.
                Attributes = new TrackChangesFor<AttributesInCreatePersonRequest>(_apiClient)
                {
                    Initializer =
                    {
                        FirstName = null,
                        LastName = "Cinderella"
                    }
                }.Initializer
            }
        }
    ]
};

var response = await _apiClient.PostOperationsAsync(operationsRequest, cancellationToken);

Unfortunately, the template we'd need to ship is rather large. We'd need to replicate any changes to it, which is a maintenance burden on our side. But what's worse is that our NuGet package becomes indirectly dependent on specific NSwag versions.

Request tracked at RicoSuter/NJsonSchema#1717.