BoKDamgaard
BoKDamgaard

Reputation: 398

Versioning in ServiceStack - again

I've read this post by @mythz https://stackoverflow.com/a/12413091/1095655, but my reputation isn't high enough to comment

I have a problem making is work. If you get a request with a version 1 DTO, the version is not present in the transmitted data, because the version 1 DTO dont have the Version property. When ServiceStack deserializes the request it creates a DTO object (version 3), with Version set to 2, and because Version is not received from the transmitted data, the Version is not overridding to 0, as expected. As I see it you need to have Version implemented from the start in your DTO, otherwise it wont work.

Hopefully I'm wrong, and someone can clarify what I'm missing? Can this be solved, as I have a DTO in production without Version implememnted?

Upvotes: 3

Views: 728

Answers (1)

mythz
mythz

Reputation: 143319

Yeah this can be tricky when you first add the Version info because you want to have the Version number in the constructor so each call is assigned its implicit version at the same time the same constructor gets used whenever you deserialize into the new DTO.

Unfortunately as there is no Version in the original DTO it's not serialized onto the wire so it never overrides the implicitly assigned Version field.

To get around this (and keep the implicitly assigned version number in the constructor) you need effectively reset the version that's used in deserialization.

You can do this with the JSON/JSV serializers by overriding the JsConfig.ModelFactory on App_Start (i.e. in your AppConfig) which allows you to control the instance created for each POCO type used in deserialization.

In this case we want to reset any DTO that has a version back to 0 that way DTOs without a Version number is assigned 0 whilst DTOs with a Version number will override it:

 JsConfig.ModelFactory = type => {
    if (typeof(IHasVersion).IsAssignableFrom(type))
    {
        return () => {
            var obj = (IHasVersion)type.CreateInstance();
            obj.Version = 0;
            return obj;
        };
    }
    return () => type.CreateInstance();
};

I'm using an explicit IHasVersion interface here for simplicity but you can also easily use reflection to detect and reassign types that contain a Version number.

Here's an example of an original DTO without a Version property deserializing into a DTO with an implicitly assigned Version number:

public class Dto
{
    public string Name { get; set; }
}

public interface IHasVersion
{
    int Version { get; set; }
}

public class DtoV1 : IHasVersion
{
    public int Version { get; set; }
    public string Name { get; set; }

    public DtoV1()
    {
        Version = 1;
    }
}

Now when you Deserialize the original DTO into the newer DtoV1 it's left as 0:

var dto = new Dto { Name = "Foo" };
var fromDto = dto.ToJson().FromJson<DtoV1>();
fromDto.Version // 0
fromDto.Name    // Foo

And if you use the new DTO it populates the Version number:

var dto1 = new DtoV1 { Name = "Foo 1" };
var fromDto1 = dto1.ToJson().FromJson<DtoV1>();
fromDto.Version // 1
fromDto.Name    // Foo 1

Upvotes: 3

Related Questions