1

I derive multiple DTOs (data transfer object) from a base DTO. I have a property in base DTO (isUpdateAvailable) which is shared across all derived class. I have a method which is common for multiple use case that takes the base DTO and uses it either directly or by converting it to the respective derived DTO.

I think this is not a good c# code design. There should not be a need to convert. Moreover, I also heard that this kind of code design breaks some SOLID principle.

I have created a short sample code to describe my point. Please have a look:

 public class UpdateNotification
 {
    public void ChromeNotification(MyBaseDto baseDto, NotificationType type)
    {
        OnUpdateAvailable(baseDto, type);
    }
    public void OutlookUpdateNotification(MyBaseDto baseDto,   
  NotificationType type)
    {
        OnUpdateAvailable(baseDto, type);
    }
    public void OnUpdateAvailable(MyBaseDto baseDto, NotificationType type)
    {
        if (type == NotificationType.Chrome)
        {
            // it uses baseDto.IsUpdateAvailable as well as it downcast it 
     to DerivedAdto and uses other properties

            var derivedDto = baseDto as DerivedAdto;
        }

        if (type == NotificationType.Outlook)
        {
            // currently it just uses baseDto.IsUpdateAvailable
        }
    }
    public enum NotificationType
    {
        Chrome,
        Outlook
    }
  }

I am focussing here on the use of DTO objects which are "MyBaseDto", "DerivedAdto" and "DerivedBdto". My current structure of DTOs are as follows:

  public abstract class MyBaseDto
  {
    public MyBaseDto(bool isUpdateAvailable)
    {
        IsUpdateAvailable = isUpdateAvailable;
    }
    public bool IsUpdateAvailable { get; }
  }

  public class DerivedAdto : MyBaseDto
  {
    public DerivedAdto(bool isUpdateAvailable)
        : base(isUpdateAvailable)
    {

    }
    public string PropertyA { get; set; }
  }

  public class DerivedBdto : MyBaseDto
  {
    public DerivedBdto(bool isUpdateAvailable)
        : base(isUpdateAvailable)
    {

    }
  }

Is there a better design for these DTO classes?

Can I design something like below or can you suggest a better approach?

 public abstract class MyBaseDto
{
   public abstract bool IsUpdateAvailable { get; set;}
}

public class DerivedAdto : MyBaseDto
{
    public override bool IsUpdateAvailable { get; set;}
    public string PropertyA { get; set; }
}

  public class DerivedBdto : MyBaseDto
 {
     public override bool IsUpdateAvailable { get; set;}
 }

Thanks a lot.

Arghavan
  • 1,125
  • 1
  • 11
  • 17
App
  • 346
  • 3
  • 9

3 Answers3

4

The inheritance doesn't violate any principles. But this does:

public void OnUpdateAvailable(MyBaseDto baseDto, NotificationType type)
{
    if (type == NotificationType.Chrome)
    {
        // it uses baseDto.IsUpdateAvailable as well as it downcast it 
 to DerivedAdto and uses other properties

        var derivedDto = baseDto as DerivedAdto;
    }

    if (type == NotificationType.Outlook)
    {
        // currently it just uses baseDto.IsUpdateAvailable
    }
}

If I had to fit it to one of the principles it would be Dependency Inversion (although it's a bit of a stretch.) When you take an argument of type MyBaseDto you're depending on abstraction which could represent any number of derived classes. But as soon as you start casting it back to specific derived types, you're now tightly coupled to each of those derived types.

When you pass a parameter of type MyBaseDto, all the method should ever need to know about its type is that it is a MyBaseDto. As soon as you start casting it then you start to lose the benefit of strong typing. What if someone passes in the wrong NotificationType and as a result you try to cast baseDto to the wrong type?

Technically you could just change the type of the first parameter to object and then cast it to whatever type you expect it to be. But strong typing is supposed to ensure that you already know what type you have. And all you need to know about an object is its declared type - the type specified by the argument.

It's not a violation of the Liskov Substitution Principle because that violation would occur in the class (MyBaseDto) and its derived classes. The problem isn't in those classes, but rather how they're being used.

In a nutshell, if receive type A and then start inspecting it or casting it to see if it's really derived type B or C then something has gone wrong. All we should care about is the declared type that we receive.

Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
1

Some answers here may suggest that inheritance is perfectly fine for DTO classes on the sole bases that it allows you to write common code then use it among all of the derived classes. However, just because it is a good idea in general from an OOP point, does not mean that it is always a good idea from an OOP point, please refer to [1] for explanation.

DTOs, in my opinion (which I have come to understand based on experience and reading articles written by heavy-weight users), belong to this category as well. I definitely am not saying, You can not use inheritance, They just do not work well with inheritance very well. You end up adding some interface forcefully to use some DTOs together in some method such as IUserDto to keep user data, some other base class to commonize other DTOs usages. please refer to[2] for an actual source code which supports a game with around 4million app downloads. Therefore, here comes my other point: A DTO has to be as self-describing as possible. Look at the Object and see what "Data It Does Transfer"◀(You see what i did here?)

If you are using a DTO then there is a high probability that there is going to be something/someone which will consume that Data, at least I assume so. If not why would you transfer the data anyways. From now on, I will call the consumer as "Client". Just like the code you provided in UpdateNotification the client does not know exactly which DTO it has, so try your way out by casting, which pointed out by @Scott Hannen's answer clearly is a code-smell.

For extra information, One of the pronlems which we encountered during development with inherited DTOs was that when we serialized from a base class using some famous serializers, they failed (by design) to serialize to include derived properties or when we tried to deserialize into a common interface or base class they failed to include derived properties or failed completely. Some serializers include type information to solve this problem, please see [3].

[1] For example, I work for a company which makes RPG games mainly. Let's say we try to create a concrete class for a monster using inheritance with the following route GameEntity→Movable→Monster→FireMonster→Dragon. What if the planning wanted to change some parameters on Dragon during development? What about a FireGolem which would inherit from FireMonster? How about we add some FlyingMonster later in the development. Where will it go in the inheritance table? Is FireGolem going to be affected? -It likely is to be affected - That is why in gaming industry we always prefer composition over inheritance. I am not saying we do not use inheritace either.

[2] Please do not get shocked :) They also have partial classes which are generated with auto-creation tools. I dare you to debug or try to understand the hiearchy between them. Heck try to change a single property in IEntity or other class then you are going to have to fix other 50 other classes.

public interface IDto : IEntity, ICloneable, IHasId<long> {}

public abstract class DtoBase<T> : IDto where T : class, IDto, new(){}

public abstract class UserDtoBase<T> : DtoBase<T> where T : class, IDto, new()

public partial class ProfileDto : UserDtoBase<ProfileDto>

[3] After trying out several serializers, ServiceStack was the only one which de/serialize into/from interface/derived/base type correctly. I do not know about the current situation about the serializers.

Hasan Emrah Süngü
  • 3,488
  • 1
  • 15
  • 33
  • I would appreciate reason for -1 – Hasan Emrah Süngü May 30 '17 at 03:44
  • What does "DTO has to be as self-describing as possible" mean? Why is inheritance a bad idea because of this? Where in the OP's code is there anything about a "client" or a "service"? Why aren't there any good reasons for having interfaces / abstract base classes? What do you mean by "abstract base is also a contract to abide by too"? How will the DTO classes fail to de/serialize properly? And by "properly" do you mean that they will de/serialize improperly? And what does that mean? – Enigmativity May 30 '17 at 03:51
  • @Enigmativity, I do not understand why you got so angry over a question. I will update my answer to answer your questions as well but some of your questions are beyond stupid.`What does "DTO has to be as self-describing as possible" mean?`◀What is there to not understand here? – Hasan Emrah Süngü May 30 '17 at 03:53
  • I don't know what you mean by "DTO has to be as self-describing as possible". I know what a DTO is and I know what "self-describing" means. But I don't know why a DTO has to be as self-describing as possible means or why that's important. – Enigmativity May 30 '17 at 03:57
  • It's not my needs - the questions and answers here are for the community. You asked for the reason for the down-vote and I gave you them. – Enigmativity May 30 '17 at 04:02
  • 2
    @Enigmativity, Are you really saying that I have not answered? I think you are saying this on purpose. Second paragraph clearly explains "What does "DTO has to be as self-describing as possible" mean?" Other one, "Where in the OP's code is there anything about a "client" or a "service"?" I also explained this please see client consumer part? "How will the DTO classes fail to de/serialize properly? "And by "properly" do you mean that they will de/serialize improperly?" Look at Extra information?! Which ones are hard to understand, I will simplify it for you. – Hasan Emrah Süngü May 30 '17 at 07:35
  • Honestly, I'm not saying it on purpose. With regard to the DTO/Self-describing question - you have merely restated your assertion that they must be - you haven't explained why at all. There's nothing in the question about a "client" so to introduce a client you'd need to be very clear as to how it impacts on the use of inheritance with DTOs. And your serialization merely says that some libraries work and some don't - but again you'd have to be very clear on how this impacts inheritance of DTOs. – Enigmativity May 30 '17 at 07:45
  • @Enigmativity, You are 60K+ user. So you are telling me that you fail to see the importance of **failing** to serialize **properties of a derived class** and deserializing with **missing information** into a base class. `if(useDTO){serializeDTOWithMissingInfo();)`←Maybe this is better? I am not sure – Hasan Emrah Süngü May 30 '17 at 07:51
  • You're missing my point. Whether or not de/serialization works doesn't matter unless you explain why that affects the use of inheritance with DTOs. – Enigmativity May 30 '17 at 08:00
  • 2
    @Enigmativity, I never would have expected to see some internet trolls on Stackoverflow. **You** are missing my point. What is there not to understand that if you use inherited DTOs, some of the mostly used serializers will fail. I gave that as an extra information, assuming that since OP is dealing with DTOs, at some point or as of now, he/she is dealing with de/serializing too, which is very common if you work with DTOs. So if you use inherited DTOs, there was (is) a high risk, he fill face some problems. Please refrain from further answers, as I am not in the mood to feed the troll – Hasan Emrah Süngü May 30 '17 at 08:07
  • "I gave that as an extra information, assuming that since OP is dealing with DTOs, at some point or as of now, he/she is dealing with de/serializing too, which is very common if you work with DTOs. So if you use inherited DTOs, there was (is) a high risk, he fill face some problems" - That is a much better explanation. I am not trolling. I'm answering your questions. – Enigmativity May 30 '17 at 08:10
-2

Your design is perfectly fine, as far as inheritance is concerned on your DTO classes. It is exactly what inheritance is for - allowing you to write common code in a base class and then use it among all of the derived classes.

What's weird - and I'm not entirely sure what you're trying to do is with the calls to OnUpdateAvailable from ChromeNotification & OutlookUpdateNotification. In both cases you're passing in NotificationType which seems to provide the same information as you have when calling the two methods anyway.

It seems to me that you haven't designed your UpdateNotification well. Your DTO objects seem perfectly fine though.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172