Abstract Class base DTO

I came across a stack overflow article on inheritence that Mythz had commented on in 2012.

As all of my List Requests carry a common parameter, I had created a base DTO. I wanted to check whether you still recommend against this.

Here is my current structure.

DTOs:

[Route("/foos", "GET")]
public class GetFoos : GetList, IReturn<List<Foo>>
{
    public Guid FooPropA { get; set; }
}

[Route("/bars", "GET")]
public class GetBars : GetList, IReturn<List<Bar>>
{
    public Guid BarPropA { get; set; }
}

Base Class:

public class GetList
{
    public Guid CommonPropX{ get; set; }
    
}
1 Like

Its worth mentioning that my WSDL has changed:

From:

The following routes are available for this service:
GET /foos
GetFoos Parameters:
NAME PARAMETER DATA TYPE REQUIRED DESCRIPTION
CommonPropX query Guid No
FooPropA query Guid No

TO

The following routes are available for this service:
GET /foos
GETLIST Parameters:
NAME PARAMETER DATA TYPE REQUIRED DESCRIPTION
CommonPropX query Guid No
FooPropA query Guid No

If you do have a base class it’s recommended that it’s abstract as some serializers/Add ServiceStack Reference languages only support inheritance from base abstract classes.

Generally I’d still advise using inheritance sparingly as your DTOs are declarative Service Contracts and hiding them behind multiple layers of inheritance hides their definition making it harder to be able to determine the schema of your Services (i.e. what your Service accepts and returns) by looking at the Request / Response DTOs. If you need to force conformance across multiple DTOs I’d prefer to use Interfaces, but still have the properties declared in each DTO.

But the primary thing you should avoid in DTOs is having base class or interface properties, e.g:

public class Request
{
     public BaseClass Prop1 { get; set; }
     public IMyInterface Prop2 { get; set; }
}

As it’s impossible to determine what the type of each property is by statically analyzing the Type definition. In these cases each serializer/format/language has to emit their own proprietary serialization extensions on the wire to be able to handle it which inhibits their interoperability whilst other serializers don’t support it at all or require other non-standard attributes to declare list of known sub types.

3 Likes

We are forced to include an abstract class in a DTO. Not use it as a base class rather include it. This is a requirement from another vendor on what the server response must look like. ServerStack serialization fails to serialize the properties from the abstract class. Newtonsoft work just fine. This is a huge problem for us because we don’t control the client so the response must be just so. We have work arounds but none are pretty. I have looked at the the customizations available for serialization but none seem to solve my problem. Details are also in this SO post. Appreciate any help.

steve

When posting issues, please make sure to include a small code example that can be run to repro the issue.

Thank you for your reply. The serialization works on test classes I create and fails on the 3rd party types. I have no problem creating a sample – I already have one knowing you would ask – but I have to ship the entire runtime and sln. Its 100’s of files. As an alternative I can offer just the one DLL with the types in it and from that you may be able to glean what the problem is. You tell me which you prefer and it shall be done.

Why do you have to provide any .dll’s? can’t you provide just the source code of a minimal verifiable example that repro’s the issue? It’s a prerequisite for being able to identify and resolve issues.

If you don’t have the original source code you should be able to use the decompiler built into JetBrains Rider or their free dotPeek tool to view the source code and grab just the code fragment you need to create a mininal isolated repro.

The type is buried in another vendor’s SDK. We don’t have source to the SDK. I have used dotpeek to look at the type in question and nothing stands out as unusual. It is not a lightweight type. It is fundamental to how their SDK works and we are supposed to integrate with their SDK. Trust me, I wish I could send 10 lines of code unfortunately not the case. I can send you my test but I won’t build/run without Lead’s runtime. Is there any diagnostic or tracing I can enable there to at least point me the correct direction?

You can try to debug the decompiled sources, i.e. step debug using JetBrains Rider and put a breakpoint where the Exception is thrown.

But if this is for serializing a 3rd Party library and not your own DTOs why not just use JSON .NET if it works?

I’m all for using JSON.NET if possible. I tried that. I could not get the return type of the route correct. I tried serializing the object in JSON.NET (which worked ) then returning that string. The ServiceStack controller serialized the string, effectively escaping all the "'s with " invalidating the return type. If there is a way to get the controller return the string untouched I’ll try that. Remember, I cant mod the client either.

If this works that would be great. Again, I tried many things, this being one with no success. Long term, I would like to know why this is failing. This is a key piece of our system and its risky that is different than everything else.

Also, there is no exception. That would be helpful. The serialization runs with no error. In the sample output you can see it spits out the __type info and nothing else.

Is there anyway to create a string on the server the contains a serialized object and return it without the controller re-serializing it again?

Thanks for your help and timely replies.

Steve

This is the code that works in MVC. The key is returning a string and telling MVC not to mess with it. would love to know what the corresponding code in service stack is.

// CANNOT return a JsonResult. the LEAD document type causes a cir-ref json exception and in MVC
// there is no easy way to solve. tried bringing in WEBAPI so we could globally set the
// json setting and that failed with mis-matched DLLS. MUST return a string.
public async Task LoadFromUri(LoadDocumentFromUriRequest loadDocumentFromUriRequest)
{
var appData = Server.MapPath("~/App_Data");
var testFileName = System.IO.Path.Combine(appData, “test.jpg”);

        var director = new ImagingDirector();

        var retval = new LoadDocumentFromUriResponse(1, 1, false);

        retval.document = await director.LoadFromURIAsync(new DocumentIdentifier("1-1_1_1_1"), testFileName);

        // MUST use newtonsoft
        string json = JsonConvert.SerializeObject(retval);
        // MUST set content-type otherwise LEAD on the client doesn't like it.  default is text
        Response.ContentType = "application/json";
        return json;
    }

See Customize HTTP Responses for different ways to return a custom HTTP Response, e.g. you can do the same in a ServiceStack Service:

public object Any(ReturnJson request)
{
    Response.ContentType = MimeTypes.Json;
    return json;
}

You could also set the mime type with an attribute:

[AddHeader(ContentType = MimeTypes.Json)]
public object Any(ReturnJson request) => json;

Or write directly to the response:

public async Task Any(WriteJson request)
{
    Response.ContentType = MimeTypes.Json;
    await Response.WriteAsync(json);
}

But the recommendation is for your Service Model DTOs which defines your Services contract to be in a dependency-free project to ensure their interoperability (i.e. one of the core tenants of a Service) so that typed clients can consume APIs without coupling to binary dependencies and specific serialization library implementations. It’s less of an issue if your API consumers are just parsing the raw JSON, but I’d consider copying the data you need from the model into your own DTOs and returning those instead so that the API can make use of ServiceStack Metadata services like Add ServiceStack Reference.

As for investigating the issue, you can try enabling StrictMode to force ServiceStack.Text to throw on Error:

Env.StrictMode = true;

Note that ServiceStack.Text is only for serializing serializable models so will not serialize models with circular dependencies.

Thank you for the recommendations on the controller. I may try that or leave as is for now.

Long term, MVP1, the design all along is to have zero dependencies on our client side POCOs as you mention. We are very intentional about our project level dependencies. One design rule we had for our .net proxy was no third party dependencies. We agonized over including newtonsoft even though newtonsoft has become ubiquitous. Our client side POCOs became DTO’s when we started using ServiceStack and we had the same dilemma. We are using ServiceStack.Interfaces and hopefully won’t have to package too many assemblies for our .net integrators. The same is true for the LEADDocument type. The plan was always to write a converter but the LEADDocument type has over 150 types so we were going to postpone that work. We are working with LeadTools for a solution there otherwise we have to package their runtime which is 100’s of files.

Yea, circular dependencies will be the death of me. The default serializer in MVC5.X spit out that exception that is why I had to do with string return type and use newtonsoft.

Thanks again for your help and effort.

Steve

1 Like