Getting Servicestack to Retain Type Information

Getting ServiceStack to retain type information

Inheritance in DTOs is a bad idea - DTO's should be as self-describing as possible and by using inheritance clients effectively have no idea what the service ultimately returns. Which is why your DTO classes will fail to de/serialize properly in most 'standards-based' serializers.

There's no good reason for having interfaces in DTO's (and very few reasons to have them on POCO models), it's a cargo cult habit of using interfaces to reduce coupling in application code that's being thoughtlessly leaked into DTOs. But across process boundaries, interfaces only adds coupling (it's only reduced in code) since the consumer has no idea what concrete type to deserialize into so it has to emit serialization-specific implementation hints that now embeds C# concerns on the wire (so now even C# namespaces will break serialization) and now constrains your response to be used by a particular serializer. Leaking C# concerns on the wire violates one of the core goal of services for enabling interoperability.

As there is no concept of 'type info' in the JSON spec, in order for inheritance to work in JSON Serializers they need to emit proprietary extensions to the JSON wireformat to include this type info - which now couples your JSON payload to a specific JSON serializer implementation.

ServiceStack's JsonSerializer stores this type info in the __type property and since it can considerably bloat the payload, will only emit this type information for types that need it, i.e. Interfaces, late-bound object types or abstract classes.

With that said the solution would be to change Animal to either be an Interface or an abstract class, the recommendation however is not to use inheritance in DTOs.

ServiceStack - Force generation of Typescript types for certain classes

I'd highly recommend against using interfaces in DTOs, it's fine to have multiple Request DTOs implement the same interface, but it shouldn't be used in place of a concrete DTO.

Having said that you could create a Dummy service that holds a reference to all your DTOs, e.g:

public class DummyHolder : IReturnVoid
{
public GadgetDto GadgetDto { get; set; }
//...
}

public class DummyService : Service
{
public void Any(DummyHolder request){}
}

Alternatively you can add them to ExportTypes in the pre-defined NativeTypesFeature in your AppHost.Configure():

var nativeTypes = this.GetPlugin<NativeTypesFeature>();
nativeTypes.MetadataTypesConfig.ExportTypes.Add(typeof(GadgetDto));

reconstituting property of type object with Servicestack.Text

This is not supported in ServiceStack JSON Serializer which looks at the target type that it's deserializing into for info on how to coerce the JSON value.

Because you're using an object property there is no type info so it then fallsback and looks for existence of a __type property for this info, but ServiceStack never emits __type information for ValueTypes as it considerably bloats the payload.

With no type info to go on, the serializer just leaves the value as a string which is an object.

I recommend avoiding the use of interfaces, late-bound object types, etc which are a bad idea to have on DTOs, but for this you could create an extension method that inspects the string value and returns an instance of the correct type based on the contents, e.g:

var dto = new WildCard { AnyObject = 1 };

dto.AnyObject.AsJsonValue(); //Extension method that

"1" -> int 1
"true" -> bool true
"anything else" -> string "anything else"

The only issue with this is not being able to send a string literal containing a number or boolean value as it always gets coerced into their respective types.

ServiceStack.Text how to get subclass values to be serialized?

Ok, found it, for this to work the PropertyDataEx needs to be abstract.

ServiceStack: Property in request DTO becomes null if type is abstract

You would need to enable JsConfig.IncludeTypeInfo = true; on the client side, so the serializer includes the type information with the request. This will add an extra property (__type) with the type definition so the service knows what to type it as.

It fails currently because requests by default don't provide type information to deserialize the object into the class that implements the interface. This was an issue that was previously raised.

The problem is the when the JSON client makes the request, it will serialize up the a class that implements IThisOrThat such as your This class. But when it gets to the other end ServiceStack.Text doesn't know what to deserialize the object into. The type information is lost so it doesn't know what kind of IThisOrThat it is. So without the additional __type information property in the request this is happening:

Scenario:

interface ISomething
{
string Name;
}

class MySomething : ISomething
{
public string Name { get; set; }
public int Age { get; set; }
}

class MySomethingElse : ISomething
{
public string Name { get; set; }
public int Size { get; set; }
}

Then you make the call from your JsonServiceClient using a typed object

client.Get(new MySomething { Name: "Duck", Age: 20 });

The JSON that is sent would be { "Name":"Duck", "Age":20 } what type does the deserialiser choose now? It could be an MySomething or a MySomethingElse, or even another ISomething that it just doesn't know about yet. So because it can't decide the result is simply null.

Generally interfaces and DTOs don't mix, see here.

ServiceStack.OrmLite: Selecting POCOs where Type is determined in runtime (inheritance)

There is very limited support for using runtime types in OrmLite.

See OrmLite's Untyped API Support which gives you access to some Insert, Update and Delete APIs when dealing with runtime types.

But SELECT's need to specify the concrete Type to select into, i.e. you could select into the base class with:

var results = db.Select<BaseClass>(typeOfObject);

But results would only populate the BaseClass properties.

One potential solution is to use the Dynamic Dictionary ResultSets API to select results into a list of object dictionaries, e.g:

 List<Dictionary<string,object>> results = db.Select<Dictionary<string,object>>(q);

Then use FromObjectDictionary Reflection Utils to convert it to a late-bound Type, e.g:

List<BaseClass> customers = results.Map(x => (BaseClass)x.FromObjectDictionary(customerType));

Quoting sources

(I have also noted that Mythz has said that using inheritance when persisting data is a bad idea and that he proposes to "flatten" the hierachy. However, I would stick my neck out and say that when dealing with Object Oriented languages, inheritance and polymorphism is unavoidable and not something 99.9% of the coders out there can do away with ;-))

If you're going to quote someone, please do so verbatim including a link to the source you're quoting as this is a misrepresentation of what I've said.

You're likely referencing my answer highly recommending against using inheritance and base type properties and unknown late-bound types like interfaces and objects in DTOs. This is to avoid coupling to specific serialization implementations and is a major source of runtime serialization issues which is contra to the goals of creating well-defined interoperable Services. You can avoid this guidance but your services will only work with specific serializer implementations, fail in different languages and have limited metadata support as they'll be unable to statically infer the holes in your Service contract from using unknown types.

But this quote doesn't have anything to do with inheritance in OrmLite where your POCOs can happily have any levels of inheritance. The issue is that you're trying to query an unknown late-bound Type where as OrmLite is a code-first ORM with a Typed API that needs access to the concrete type in order to support its Typed Expression API and populate its typed Results. My answer above includes OrmLite's limited support for untyped access.

ServiceStack Serialize and Deserialize Dictionary with Objects

While what Demiz said is true - that Inheritance in DTO is bad, I would really want to post a more accurate answer for the question just in case anyone really needs it.

Set the following flags:

JsConfig.ExcludeTypeInfo = false; //this is false by default
JsConfig.ConvertObjectTypesIntoStringDictionary = true; //must set this to true

For List of objects that happens to be serialized, you would need to deserialize it as a list of objects first and cast each one of it back to the original class:

//save to database
var id = db.Save(john);

//retrieve it back
var retrieved = db.SingleById<Person>(id);

//try to access the Address List
var name = retrieved.Name; //this gives "John"
//cast it to list of objects first
var tempList = retrieved.ExtraParameters["AddressList"] as List<object>;
//cast each of the objects back to their original class;
var address = tempList.Select(x=> x as Address);

Hope this one helps whoever needs it in the future.

ServiceStack benchmark continued: why does persisting a simple (complex) to JSON slow down SELECTs?

I've managed to download and profile this solution which highlighted the cause of the issue of not caching the type accessors of late-bound types which is resolved with this commit.

With this change performance for loading 10000 rows with complex types reduced from 11,765ms to 669ms (on my iMac 5k) as seen below:

Sample Image

This change is available from v5.1.1 that's now available on MyGet.

Note: I've removed the JsConfig.IncludeTypeInfo line below:

JsConfig.IncludeTypeInfo = true;

Which forces the Serializers to emit type info for every object which increases the payload size and decreases performance. ServiceStack.Text will already emit the type info when it's needed, i.e. for object, interfaces and abstract classes, so you should rarely force it yourself unless it's absolutely needed, as it can have a major detrimental impact on performance.

Ideally your DTO's should not use interfaces, late-bound objects or inheritance but if you are consider making the base types abstract to force the type info to only where it's needed, instead of always emitting them.

ServiceStack JsonSerializer not serializing object members when inheritance

By default ServiceStack only serializes public properties. To serialize public fields as well you need to set:

JsConfig.IncludePublicFields = true;

Also if you want to use inheritance you need to make the sub class abstract or an interface. See this answer for more info.



Related Topics



Leave a reply



Submit