skip to Main Content

I’m sorry for the bad title but I really don’t know how to put this any other way. The detailed problem is as follows:

I have a .NET API with Controllers. I have a controller that has a reference to a service of generic type Entity and that returns an IEnumerable of generic Dto<>‘s like so:

public class SomeController {
private readonly Service<SomeEntity> _someService;

    //constructor
    
    [HttpGet]
    public async Task<IEnumerable<Dto<SomeEntity>>> AsyncGetSomeData() {
        return await _someService.AsyncGet();
    }

}

The Service interface looks like this; it’s purpose is to call a repository for entities and convert the entities to dto’s in a way that a different Dto could be used for a given Entity:

public interface Service<T> where T : Entity
{
    Task<IEnumerable<Dto<T>>?> AsyncGet();
}

The Entity interface looks like this (for now):

public interface Entity {}

The Dto interface looks like this:

public interface Dto<T> where T : Entity.Entity {}

Implementations of the Entity interface can look like this:

public record SomeEntity (Guid id) : Entity

Implementation of the Dto can look like this:

public record SomeDto(string Id) : Dto<SomeEntity>;

When I return the objects in the way the controller is set up now (as described), empty json objects are returned. This is because the json serializer is not able to figure out that Dto<SomeEntity> can be serialized as SomeDto. This is proven by the fact that changing the signature to return IEnumerable<SomeDto> and casting the items in IEnumerable<Dto<SomeEntity>> to SomeDto does in fact serialize the objects correctly.

My question is: is it possible to change a setting or map Dto<SomeEntity> to SomeDto or something else to avoid having to change signatures and casting?

I have tried finding examples of something like this (Google and StackOverflow), but as you can see the problem is quite complicated to encapsulate in one sentence (I am accepting title suggestions to improve this question). I hope someone can either point me to a duplicate question or give me an answer.

EDIT:
I am trying to use the JsonDerivedTypeAttribute. I have two entity types and corresponding dto’s:

public sealed record CourseDto() : Dto<CourseEntity>

public sealed record CourseDefinitionDto() : Dto<CourseDefinitionEntity>

Then, I have defined the JsonDerivedTypeAttribute on the Dto interface:

[JsonDerivedType(derivedType: typeof(CourseDto), "a")]
[JsonDerivedType(derivedType: typeof(CourseDefinitionDto), "b")]
public interface Dto<T> where T : Entity.Entity;

No matter what I use in the typeDiscriminator parameter, I get the following error:

"System.InvalidOperationException: Specified type ‘Application.Data.Dto.CourseDto’ is not a supported derived type for the polymorphic type ‘Application.Data.Dto.Dto`1[Application.Data.Entity.CourseDefinitionEntity]’. Derived types must be assignable to the base type, must not be generic and cannot be abstact classes or interfaces unless ‘JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor’ is specified."

2

Answers


  1. If you are using System.Text.Json you should have a look at JsonDerivedTypeAttribute and JsonConverterAttribute

    Here is a quick sample of what you could do with JsonDerivedTypeAttribute

    public interface Entity { }
    
    [JsonDerivedType(typeof(SomeDto), "#some")]
    [JsonDerivedType(typeof(AnotherDto), "#another")]
    public interface Dto { }
    
    public interface Dto<T> : Dto where T : Entity { }
    
    public class SomeEntity : Entity { }
    
    public class AnotherEntity : Entity { }
    
    public class SomeDto : Dto<SomeEntity> { }
    
    public class AnotherDto : Dto<AnotherEntity> { }
    

    A sample to use

    Dto some = new SomeDto();
    Dto another = new AnotherDto();
    
    var options = new JsonSerializerOptions();
    var someJson = JsonSerializer.Serialize(some, options);
    var anotherJson = JsonSerializer.Serialize(another, options);
    
    Console.WriteLine(someJson);
    Console.WriteLine(anotherJson);
    
    var someDto = JsonSerializer.Deserialize<Dto>(someJson);
    var anotherDto = JsonSerializer.Deserialize<Dto>(anotherJson);
    
    Console.WriteLine(someDto?.GetType().Name);
    Console.WriteLine(anotherDto?.GetType().Name);
    

    Output

    {"$type":"#some"}
    {"$type":"#another"}
    SomeDto
    AnotherDto
    

    EDIT

    Here is a hint to get your controller able to return Dto interface, not generic Dto<T> interface.
    You can do this because IEnumerable<T> interface is covariant.

    public interface Service<T> where T : Entity
    {
        Task<IEnumerable<Dto<T>>?> GetAsync();
    }
    

    Your action signature becomes

    [HttpGet]
    public async Task<IEnumerable<Dto>?> GetSomeDataAsync()
    {
        return await _someService.GetAsync();
    }
    

    I’ve switched Async word in method’s name, to be more compliant with C# naming convention.

    Login or Signup to reply.
  2. My advice is you give it both classes:

    public class MyClass<Tin,Tout>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search