I have a TimeDetails class that has a collection of TimeRanges. This class is serialized and store to DB.
The list used to be of type DateTimeRange. Now I have to add a new property to it (SomeId). So I inherited it to AvailableTimeRange and added SomeId property to it. (I have to leave the DateTimeRange as-is since its used in other places.
The deserialization of the old rows (having DateTimeRange property type) fails.
Here is my unit test
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using mynamespace;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTestProject1
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void With_DateTimeRange()
{
var xml = "<TimeDetails xmlns=\"http://schemas.datacontract.org/2004/07/mynamespace\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"<TimeList>" +
"<DateTimeRange><From>2016-01-21T08:00:00</From><To>2016-01-21T11:00:00</To></DateTimeRange>" +
"<DateTimeRange><From>2016-07-12T06:00:00</From><To>2016-07-12T09:00:00</To></DateTimeRange>" +
"</TimeList>" +
"</TimeDetails>";
TimeDetails details = TimeDetails.FromXml(xml);
Assert.AreEqual(2, details.TimeList.Count);
}
[TestMethod]
public void With_AvailableTimeRange()
{
var xml =
"<TimeDetails xmlns=\"http://schemas.datacontract.org/2004/07/mynamespace\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">"+
"<TimeList>"+
"<AvailableTimeRange><From>2016-07-12T08:00:00</From><SomeId>100</SomeId><To>2016-07-12T09:00:00</To></AvailableTimeRange>"+
"</TimeList></TimeDetails>";
var details = TimeDetails.FromXml(xml);
Assert.AreEqual(1, details.TimeList.Count);
Assert.IsTrue(details.TimeList[0] is AvailableTimeRange);
Assert.AreEqual(100, (details.TimeList[0] as AvailableTimeRange).SomeId);
}
}
}
namespace mynamespace
{
public class TimeDetails
{
public List<AvailableTimeRange> TimeList { get; set; }
public static TimeDetails FromXml(string xml)
{
if (String.IsNullOrWhiteSpace(xml))
return null;
TimeDetails timeDetails;
byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
using (MemoryStream ms = new MemoryStream(data))
{
DataContractSerializer serializer = new DataContractSerializer(typeof (TimeDetails),
new[] {typeof (AvailableTimeRange), typeof (DateTimeRange)});
timeDetails = (TimeDetails) serializer.ReadObject(ms);
}
return timeDetails;
}
}
public class DateTimeRange
{
public DateTime From { get; set; }
public DateTime To { get; set; }
}
public class AvailableTimeRange : DateTimeRange
{
public long? SomeId { get; set; }
}
}
For backward compatibility you should leave list of DateTimeRange
- it can store both base and derived types.
public List<DateTimeRange> TimeList { get; set; }
In xml use type
attribute to identify AvailableTimeRange
instances:
<DateTimeRange i:type=\"AvailableTimeRange\"><From>2016-07-12T08:00:00</From><To>2016-07-12T09:00:00</To><SomeId>100</SomeId></DateTimeRange>
Beware - order of members in xml is important.