Search code examples
c#jsonasp.net-coreasp.net-core-webapinsjsonserialization

Guid does not get transferred to service correctly using ASP.NET Core web service


I have the following problem: I have an IDto interface and a Dto base class implementing said interface.

The base class overrides an id and a ManualId which are defined in the HasId base class.

Then I also have an AuthorDto which inherits from the Dto base class.

Now when I try to perform a http request (POST method) to the service, the ManualId does not get transferred to the service correctly even though the JSON string contains the correct ManualId.

Please help.

My IDto interface:

public interface IDto : IHasId 
{
    IDto CloneData();
}

My dto base class:

public abstract class Dto : HasId, IDto 
{
    [DataMember]
    public override long? Id { get; set; }

    [DataMember]
    public override Guid ManualId { get; protected set; } = Guid.NewGuid();

    public abstract IDto CloneData();

    protected bool Equals(Dto other) 
    {
        return base.Equals(other) && Id == other.Id;
    }

    public override bool Equals(object obj) 
    {
        if(ReferenceEquals(null, obj)) return false;
        if(ReferenceEquals(this, obj)) return true;
        if(obj.GetType() != GetType()) return false;

        return Equals((Dto)obj);
    }

    public override int GetHashCode() 
    {
        unchecked 
        {
            return(base.GetHashCode() * 397) ^ Id.GetHashCode();
        }
    }
}

The HasId class:

public abstract class HasId : IHasId 
{
    public abstract long? Id { get; set; }
    public abstract Guid ManualId { get; protected set; }

    protected bool Equals(HasId other) 
    {
        return Id == other.Id && ManualId.Equals(other.ManualId);
    }

    public override bool Equals(object obj) 
    {
        if(ReferenceEquals(null, obj)) return false;
        if(ReferenceEquals(this, obj)) return true;
        if(obj.GetType() != GetType()) return false;

        return Equals((HasId) obj);
    }

    public override int GetHashCode() 
    {
        unchecked 
        {
            return(Id.GetHashCode() * 397) ^ ManualId.GetHashCode();
        }
    }
}

The class doing the HttpRequest:

public class HttpHelper 
{
    private readonly HttpClient _client = new();

    public async Task<TResult> GetObject<TResult>(string requestUri) 
    {
        var response = await _client.GetAsync(requestUri);
        response.EnsureSuccessStatusCode();
        var valueInJsonFormat = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<TResult>(valueInJsonFormat);
    }

    public async Task<TResult> PostObject<T, TResult>(T obj, string requestUri) 
    {
        var json = JsonConvert.SerializeObject(obj);
        var data = new StringContent(json, Encoding.UTF8, "application/json");
        var response = await _client.PostAsync(new Uri(requestUri), data);

        if (response.StatusCode != HttpStatusCode.OK) 
        {
            var httpContent =
                JsonConvert.DeserializeObject<HttpResponseContent>(await response.Content.ReadAsStringAsync());

            try 
            {
                response.EnsureSuccessStatusCode();
            }
            catch(Exception e) 
            {
                throw new Exception(httpContent?.Detail ?? e.Message, e);
            }
        }

        var result = await response.Content.ReadAsStringAsync();

        return JsonConvert.DeserializeObject<TResult>(result);
    }
}

This is the JSON-String:

[
{"Author":
  {
    "Id":1,
    "Settings":
      {
        "Id":1,
        "ArchiveXml":false,
        "CreatePdfImmediately":false,
        "OverwriteExistingXml":false,
        "SaveSimilarTopic":false,
        "PdfNameTemplate":null,"PdfPath":null, 
        "ManualId":"624fa6e3-0864-40bf-87c3a7fcaa1421d4"
      }, 
    "Name":"Admindef", 
    "IsAdmin":true,
    "PublicKey" : PUBLIC-KEY HERE (removed since its not relevant),
    "ManualId":"f15cb1a5-2d66-4ca9-8dc6-1bf5c25bc7f8"},"Operation":0}]

Solution

  • As far as I understand, you are trying to POST a JSON string with a ManualId in it, but in the service, you don't get the same ManualId as in your json.

    I think the problem is that your method to set the ManualId is protected.

    See here:

    public abstract Guid ManualId { get; protected set; }
    

    Protecting it means it won't be set from the deserialisation.

    You should use the default get and set pair like here:

    public abstract Guid ManualId { get; set; }
    

    See if this fixes your issue?

    This change can also be related to your Dto class and your HasId class.


    If you want to be able to set the property from the JSON but keep it protected in the code, then you should create a constructor with the parameters you want to deserialize.

    Deserialisation follows the following principle:

    JSON deserialisation

    And you need to be sure that in this graph you will end up calling a constructor with parseable parameters.