最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Using DTOs with Polymorphic Models in .NET Core - Stack Overflow

programmeradmin4浏览0评论

I am using JsonDerivedType for polymorphic models in .NET Core, and I need to map a hierarchy of complex model objects to a set of DTO objects for serialization. Specifically, I want to use DTOs with polymorphic types when creating or updating different types of Notification ( NotificationAlert, SafetyAlert, etc.).

[JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")]
[JsonDerivedType(typeof(NotificationAlert), typeDiscriminator: "notificationAlert")]
[JsonDerivedType(typeof(SafetyAlert), typeDiscriminator: "safetyAlert")]
[JsonDerivedType(typeof(perationalAlert), typeDiscriminator: "operationalAlert")]
public abstract class Notification
{
    public int NotificationId { get; set; }
    public DateTime CreatedDate { get; set; } = DateTime.Now;
    public Department department { get; set; }

}


public class NotificationAlert : Notification
{
    public string AlertMessage { get; set; }
}

Currently, my NotificationAlert class is working like this, and the JSON is being serialized correctly:

{
  "NotificationId": 1,
  "CreatedDate": "2025-02-17T12:00:00",
  "Type": "notificationAlert",
  "AlertMessage": "This is an alert message",
  "Department": {
    "Id": 101,
    "Name": "HR"
  }
}

However, I want to use DTOs to better manage the structure and serialization of the notification. Here's the DTO I have: I tried to use AutoMapper, but I encountered the following error: Cannot create an instance of abstract type is there some specific mapping I should do to resolve this issue

public class NotificationCreateRequestDTO
{
     public DateTimeOffset DateTime { get; set; }
    public string Department { get; set; }
    public string Type { get; set; }
    public string AlertMessage { get; set; }

}

And the expected JSON for this DTO:

{
  "DateTime": "2025-02-17T12:00:00+00:00",
  "Department": "HR",
  "Type": "notificationAlert",
  "AlertMessage": "This is an alert message"
}

Here are the mappings I set up:


CreateMap<NotificationCreateRequestDTO, Notification>()
    .Include<NotificationCreateRequestDTO, NotificationAlert>()
    .ForMember(dest => dest.NotificationId, opt => opt.MapFrom(src => src.NotificationId))
    .ForMember(dest => dest.CreatedDate, opt => opt.MapFrom(src => src.CreatedDate))
    .ForMember(dest => dest.Department, opt => opt.MapFrom(src => src.Department));  

CreateMap<NotificationAlert, NotificationCreateRequestDTO>()
    .ForMember(dest => dest.Notification, opt => opt.MapFrom(src => src))
    .ForMember(dest => dest.Department, opt => opt.MapFrom(src => src.Department));  

I am using JsonDerivedType for polymorphic models in .NET Core, and I need to map a hierarchy of complex model objects to a set of DTO objects for serialization. Specifically, I want to use DTOs with polymorphic types when creating or updating different types of Notification ( NotificationAlert, SafetyAlert, etc.).

[JsonPolymorphic(TypeDiscriminatorPropertyName = "Type")]
[JsonDerivedType(typeof(NotificationAlert), typeDiscriminator: "notificationAlert")]
[JsonDerivedType(typeof(SafetyAlert), typeDiscriminator: "safetyAlert")]
[JsonDerivedType(typeof(perationalAlert), typeDiscriminator: "operationalAlert")]
public abstract class Notification
{
    public int NotificationId { get; set; }
    public DateTime CreatedDate { get; set; } = DateTime.Now;
    public Department department { get; set; }

}


public class NotificationAlert : Notification
{
    public string AlertMessage { get; set; }
}

Currently, my NotificationAlert class is working like this, and the JSON is being serialized correctly:

{
  "NotificationId": 1,
  "CreatedDate": "2025-02-17T12:00:00",
  "Type": "notificationAlert",
  "AlertMessage": "This is an alert message",
  "Department": {
    "Id": 101,
    "Name": "HR"
  }
}

However, I want to use DTOs to better manage the structure and serialization of the notification. Here's the DTO I have: I tried to use AutoMapper, but I encountered the following error: Cannot create an instance of abstract type is there some specific mapping I should do to resolve this issue

public class NotificationCreateRequestDTO
{
     public DateTimeOffset DateTime { get; set; }
    public string Department { get; set; }
    public string Type { get; set; }
    public string AlertMessage { get; set; }

}

And the expected JSON for this DTO:

{
  "DateTime": "2025-02-17T12:00:00+00:00",
  "Department": "HR",
  "Type": "notificationAlert",
  "AlertMessage": "This is an alert message"
}

Here are the mappings I set up:


CreateMap<NotificationCreateRequestDTO, Notification>()
    .Include<NotificationCreateRequestDTO, NotificationAlert>()
    .ForMember(dest => dest.NotificationId, opt => opt.MapFrom(src => src.NotificationId))
    .ForMember(dest => dest.CreatedDate, opt => opt.MapFrom(src => src.CreatedDate))
    .ForMember(dest => dest.Department, opt => opt.MapFrom(src => src.Department));  

CreateMap<NotificationAlert, NotificationCreateRequestDTO>()
    .ForMember(dest => dest.Notification, opt => opt.MapFrom(src => src))
    .ForMember(dest => dest.Department, opt => opt.MapFrom(src => src.Department));  

Share Improve this question edited Feb 17 at 15:47 dbc 117k26 gold badges263 silver badges387 bronze badges asked Feb 17 at 8:40 KafkaaKafkaa 354 bronze badges 3
  • It is not clear what the actual question is. It looks like you already found the attributes needed for polymorphic serialization. You show two apparently unrelated types, neither have any logic, so both could be called a "DTO", but only one has such a postfix. You mention "alert types" but no such type is shown. What have you tried? what was the result? what result did you expect? – JonasH Commented Feb 17 at 9:17
  • 1 Or is this about mapping a hierarchy of complex model objects to a set of DTO objects for serialization? If so you should show a better example of the problem. – JonasH Commented Feb 17 at 9:20
  • @JonasH Yes, this is about mapping a hierarchy of complex model objects to a set of DTO objects for serialization – Kafkaa Commented Feb 17 at 9:36
Add a comment  | 

2 Answers 2

Reset to default 1

There are a few options:

If your 'model' classes lack logic there is little to be gained from keeping and maintaining a separate set of dto classes. This might however indicate a separate problem, see Anemic domain model.

Next simplest option is to just add methods to manually map the model to the DTO and vice versa. A simplified example might look something like this:

public class AModel
{
    public long Id { get; set; }
    public virtual ADTO ToDTO() => new() { Id = Id };
}

public class BModel : AModel
{
    public string Name { get; set; }
    public override ADTO ToDTO() => new BDTO() { Id = Id, Name = Name };
}

[JsonDerivedType(typeof(BDTO), typeDiscriminator:"BDTO")]
public class ADTO
{
    public long Id { get; set; }
    public virtual AModel ToModel() => new() { Id = Id };
}
public class BDTO : ADTO
{
    public string Name { get; set; }
    public override AModel ToModel() => new BModel() { Id = Id, Name = Name };
}

If there is an inheritance hierarchy that needs to be preserved you should have one DTO class for each model class, and use the polymorphic serialization of the DTO classes, that should make the explicit 'type'-parameter unnecessary. Your mapping methods, i.e. ToDTO/ToModel need to be virtual to map to the correct type. You can do any kind of type transformation at this stage, but you need to make sure all relevant information, including types, are preserved.

A potential downside with such manual mapping is the tight coupling between the model and DTOs. One way to avoid this is to use the visitor pattern. This allows you to create a visitor to do the mapping, and to separate this from the actual model and/or DTOs. One possible benefit of this is that the compiler can ensure that each type is mapped, therefore reducing the risk that a mapping is fotten when creating a new type.

It is also possible to implement JsonConverters for each of your types, where you have very fine grained control over how a type should be serialized/deserialized, and this may include mapping to another class and use that for serialization.

Using AutoMapper as you mention is another possible approach. I have no personal experience with this, so I cannot opine if it is suitable for your particular problem, or suggest how it should be used.

If you are unsure about the best approach I would encourage you to do some experimentation. Create some simplified test cases, test each approach and see what option you prefer.

Something like this should do the trick:

var config = new MapperConfiguration(cfg => 
cfg.CreateMap<NotificationAlert, NotificationCreateRequestDTO>()
    .ForMember(dest => dest.DateTime, opt => opt.MapFrom(src => src.CreatedDate))
    .ForMember(dest => dest.Department, opt => opt.MapFrom(src => src.department.Name))
    .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src.GetType())));

var alertNotification = new NotificationAlert();
alertNotification.NotificationId = 1;
alertNotification.CreatedDate = DateTime.Now;
alertNotification.AlertMessage = "This is an alert message";
alertNotification.department = new Department() { Id = 101, Name = "HR" };

var mapper = new Mapper(config);
var dtoObject = mapper.Map<NotificationAlert, NotificationCreateRequestDTO>(alertNotification);
发布评论

评论列表(0)

  1. 暂无评论