public static void AllowDeserialisationIfFieldCanNotBeSetIfFieldIsForAutoPropertyThatIsMarkedAsOptionalForDeserialisation() { var sourceType = ConstructType(GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", new Tuple <string, Type> [0]); var instance = Activator.CreateInstance(sourceType); var serialisedData = BinarySerialisation.Serialise(instance); const string namePropertyName = "Name"; var destinationType = ConstructType( GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", fields: new Tuple <string, Type> [0], optionalFinisher: typeBuilder => { // Need to define the field using this lambda rather than specifying it through the fields argument because we need the reference for use in the property getter var fieldBuilder = typeBuilder.DefineField(BackingFieldHelpers.GetBackingFieldName(namePropertyName), typeof(string), FieldAttributes.Private); var propertyBuilder = typeBuilder.DefineProperty(namePropertyName, PropertyAttributes.None, typeof(string), parameterTypes: Type.EmptyTypes); propertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(DeprecatedAttribute).GetConstructor(new[] { typeof(string) }), new object[] { null })); var getterBuilder = typeBuilder.DefineMethod("get_" + namePropertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(string), parameterTypes: Type.EmptyTypes); var ilGenerator = getterBuilder.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldarg_0); ilGenerator.Emit(OpCodes.Ldfld, fieldBuilder); ilGenerator.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getterBuilder); propertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(OptionalWhenDeserialisingAttribute).GetConstructor(Type.EmptyTypes), new object[0])); } ); var clone = ResolveDynamicAssembliesWhilePerformingAction( () => Deserialise(serialisedData, destinationType), destinationType ); var namePropertyOnDestination = destinationType.GetProperty(namePropertyName); Assert.Null(namePropertyOnDestination.GetValue(clone)); }
public static void IgnoreFieldsInDataNotPresentOnDeserialisationTypeWhenDeserialisationTypeConvertersUsed() { const string idFieldName = "Id"; const string nameFieldName = "Name"; var sourceType = ConstructType( GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "ClassWithIntId", new[] { Tuple.Create(idFieldName, typeof(int)), Tuple.Create(nameFieldName, typeof(string)) } ); var instance = Activator.CreateInstance(sourceType); var idFieldOnSource = sourceType.GetField(idFieldName); idFieldOnSource.SetValue(instance, 123); var serialisedData = BinarySerialisation.Serialise(instance); var destinationType = ConstructType(GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "ClassWithIntId", new[] { Tuple.Create(idFieldName, typeof(int)) }); var clone = ResolveDynamicAssembliesWhilePerformingAction( () => Deserialise(serialisedData, destinationType, new[] { NullDeserialisationTypeConverter.Instance }), destinationType ); var idFieldOnDestination = destinationType.GetField(idFieldName); Assert.Equal(123, idFieldOnDestination.GetValue(clone)); }
public static void DoNotSetFieldInDeserialisationIfItHasNonSerializedAttributeEvenIfItIsPresentInSerialisedData() { const string nameFieldName = "Name"; var sourceType = ConstructType(GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", new[] { Tuple.Create(nameFieldName, typeof(string)) }); var instance = Activator.CreateInstance(sourceType); var nameFieldOnSource = sourceType.GetField(nameFieldName); nameFieldOnSource.SetValue(instance, "Test"); var serialisedData = BinarySerialisation.Serialise(instance); var destinationType = ConstructType( GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", fields: new Tuple <string, Type> [0], optionalFinisher: typeBuilder => { // Need to define the field using this lambda rather than specifying it through the fields argument because we need to set the custom attribute on it var fieldBuilder = typeBuilder.DefineField(nameFieldName, typeof(string), FieldAttributes.Public); fieldBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(NonSerializedAttribute).GetConstructor(Type.EmptyTypes), new object[0])); } ); var clone = ResolveDynamicAssembliesWhilePerformingAction( () => Deserialise(serialisedData, destinationType), destinationType ); var nameFieldOnDestination = destinationType.GetField(nameFieldName); Assert.Null(nameFieldOnDestination.GetValue(clone)); }
public static void DeserialisationWillFailIfTypeRequiredToSetFieldValueIsNotAvailable() { // The source type will have a name that matches the deserialisation type but the "Value" property will have different types on the source and destination types // and so the deserialisation attempt should fail var valueFieldName = "Value"; var sourceModule = GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)); var nestedSourceType = ConstructType(sourceModule, "MyNestedClass", new Tuple <string, Type> [0]); var sourceType = ConstructType(sourceModule, "MyClass", new[] { Tuple.Create(valueFieldName, nestedSourceType) }); var instance = Activator.CreateInstance(sourceType); sourceType.GetField(valueFieldName).SetValue(instance, Activator.CreateInstance(nestedSourceType)); var serialisedData = BinarySerialisation.Serialise(instance); var destinationModule = GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)); var destinationType = ConstructType(destinationModule, "MyClass", new[] { Tuple.Create(valueFieldName, nestedSourceType) }); var nestedDestinationType = ConstructType(destinationModule, "MyOtherNestedClass", new Tuple <string, Type> [0]); Assert.Throws <TypeLoadException>(() => ResolveDynamicAssembliesWhilePerformingAction( () => Deserialise(serialisedData, destinationType), destinationType, nestedDestinationType ) ); }
public static void DoNotThrowWhenTryingToParseUnavailableArrayElementTypeIfThereIsNoFieldThatTheTypeWouldBeUsedToSet() { // Declare a type that has a field that is an array of another element type that is declared here. The destination type will be in an assembly that // does not have this second type in it but the destination type also won't have the field and so the deserialiser should be able to skip over the // data about the field that we don't care about. var itemsFieldName = "Items"; var sourceModule = GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)); var nestedSourceType = ConstructType(sourceModule, "MyNestedClass", new Tuple <string, Type> [0]); var nestedSourceTypeArrayType = nestedSourceType.MakeArrayType(); var sourceType = ConstructType(sourceModule, "MyClass", new[] { Tuple.Create(itemsFieldName, nestedSourceTypeArrayType) }); var sourceInstance = Activator.CreateInstance(sourceType); var itemsArray = Array.CreateInstance(nestedSourceType, 3); for (int i = 0; i < itemsArray.Length; i++) { itemsArray.SetValue(Activator.CreateInstance(nestedSourceType), i); } sourceType.GetField(itemsFieldName).SetValue(sourceInstance, itemsArray); var serialisedData = BinarySerialisation.Serialise(sourceInstance); var destinationType = ConstructType(GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", new Tuple <string, Type> [0]); var deserialised = ResolveDynamicAssembliesWhilePerformingAction( () => Deserialise(serialisedData, destinationType), destinationType ); Assert.IsType(destinationType, deserialised); }
public static void RoundTripOfStringTypeUsingFastSerialisation() { var value = typeof(string); var typeConverters = new[] { TypeTypeConverter.Instance }; var serialised = FastestTreeBinarySerialisation.GetSerialiser(typeConverters).Serialise(value); var clone = BinarySerialisation.Deserialise <Type>(serialised, typeConverters); Assert.Equal(value, clone); }
private static void AssertCanGenerateCorrectMemberSetter( object source, IFastSerialisationTypeConverter[] typeConverters, int expectedNumberOfMemberSettersGenerated, int expectedNumberOfMemberSettersThatCanNotBeGenerated) { // TryToGenerateMemberSetters will try to return member setters for each type that it encountered while analysing the source type - for example, if // source is an instance of "PersonDetails" and if "PersonDetails" has an int Key property and a "NameDetails" Name property where "NameDetails" is // a class with a single string property "Name" then TryToGenerateMemberSetters will try to generate member setters for both the "PersonDetails" // and "NameDetails" classes (it may not succeed, in which case it may return zero or one member setters, or it may succeed completely and return // two member setters). It won't return member setters for values that have first class IWrite support (primitives, strings, DateTime, etc..) var sourceType = source.GetType(); var deepMemberSetterCache = new ConcurrentDictionary <Type, DeepCompiledMemberSettersGenerationResults>(); var memberSetterDetailsForAllTypesInvolved = GetMemberSettersFor(sourceType, typeConverters, deepMemberSetterCache); Assert.NotNull(memberSetterDetailsForAllTypesInvolved); // We should always get a non-null reference for this (but doesn't hurt to confirm) // Try to get member setter for the source type if (!memberSetterDetailsForAllTypesInvolved.MemberSetters.TryGetValue(sourceType, out var memberSetterDetailsForType)) { memberSetterDetailsForType = null; } Assert.NotNull(memberSetterDetailsForType); // We should know how many member setters we expected to be generated (and how many it's not possible to generate), so let's confirm Assert.Equal(expectedNumberOfMemberSettersGenerated, memberSetterDetailsForAllTypesInvolved.MemberSetters.Count(kvp => kvp.Value != null)); Assert.Equal(expectedNumberOfMemberSettersThatCanNotBeGenerated, memberSetterDetailsForAllTypesInvolved.MemberSetters.Count(kvp => kvp.Value == null)); byte[] serialised; using (var stream = new MemoryStream()) { // See notes in SimpleMemberSetterCompilationTests's "AssertCanGenerateCorrectMemberSetter" method - this code is relying more on implementation // details that I would like but it seems like the least of all evils to do so foreach (var typeName in memberSetterDetailsForAllTypesInvolved.TypeNamesToDeclare) { var typeNameBytes = new[] { (byte)BinarySerialisationDataType.FieldNamePreLoad }.Concat(typeName.AsStringAndReferenceID).ToArray(); stream.Write(typeNameBytes, 0, typeNameBytes.Length); } foreach (var fieldName in memberSetterDetailsForAllTypesInvolved.FieldNamesToDeclare) { var fieldNameBytes = new[] { (byte)BinarySerialisationDataType.FieldNamePreLoad }.Concat(fieldName.AsStringAndReferenceID).ToArray(); stream.Write(fieldNameBytes, 0, fieldNameBytes.Length); } var writer = new BinarySerialisationWriter(stream); writer.ObjectStart(source.GetType()); memberSetterDetailsForType(source, writer); writer.ObjectEnd(); serialised = stream.ToArray(); } var clone = BinarySerialisation.Deserialise <object>(serialised, typeConverters.OfType <IDeserialisationTypeConverter>().ToArray()); if (!ObjectComparer.AreEqual(source, clone, out var differenceSummaryIfNotEqual)) { throw new Exception("Clone failed: " + differenceSummaryIfNotEqual); } }
static void Main() { var value = 32; var serialiser = Serialiser.Instance; var serialisedData = BinarySerialisation.Serialise(value); var clone = BinarySerialisation.Deserialise <int>(serialisedData); Console.WriteLine("Cloned value: " + clone); Console.ReadLine(); }
public void CircularReferenceThrows() { var source = new Node(); source.Child = source; Assert.Throws <CircularReferenceException>(() => { BinarySerialisation.Serialise(source, new ISerialisationTypeConverter[0], _referenceReuseStrategy); }); }
public static T Clone <T>( T value, ISerialisationTypeConverter[] serialisationTypeConverters, IDeserialisationTypeConverter[] deserialisationTypeConverters, ReferenceReuseOptions referenceReuseStrategy) { return(BinarySerialisation.Deserialise <T>( BinarySerialisation.Serialise(value, serialisationTypeConverters, referenceReuseStrategy), deserialisationTypeConverters )); }
public static void AllowDeserialisationOfOldDataTypeIfDeprecatedFieldsAreMappedOnToTheirReplacements() { var sourceType = ConstructType(GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", new[] { Tuple.Create("NameOld", typeof(string)) }); var instance = Activator.CreateInstance(sourceType); var nameFieldOnSource = sourceType.GetField("NameOld"); nameFieldOnSource.SetValue(instance, "Test"); var serialisedData = BinarySerialisation.Serialise(instance); var destinationType = ConstructType( GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", fields: new Tuple <string, Type> [0], optionalFinisher: typeBuilder => { // Need to define the field using this lambda rather than specifying it through the fields argument because we need the reference to build the property var nameNewfieldBuilder = typeBuilder.DefineField("NameNew", typeof(string), FieldAttributes.Public); // The destinationType has a field "NameNew" that replaces the "NameOld" field on the sourceType. To communicate this to the serialiser, the destinationType also has // a "NameOld property whose getter returns the "NameNew" value and whose setter sets the "NameNew" value and the property has [Deprecated(ReplacedBy: "NameNew")] on // it. When data serialised from the sourceType is deserialised as the destinationType, the "NameOld" value in the serialised data will be used to set the "NameNew" // property and the destinationType will be successfully initialised (even though the "NameNew" field was not set directly). var propertyBuilder = typeBuilder.DefineProperty("NameOld", PropertyAttributes.None, typeof(string), parameterTypes: Type.EmptyTypes); var getterBuilder = typeBuilder.DefineMethod("get_NameOld", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(string), parameterTypes: Type.EmptyTypes); var ilGeneratorForGetter = getterBuilder.GetILGenerator(); ilGeneratorForGetter.Emit(OpCodes.Ldarg_0); ilGeneratorForGetter.Emit(OpCodes.Ldfld, nameNewfieldBuilder); ilGeneratorForGetter.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getterBuilder); var setterBuilder = typeBuilder.DefineMethod("set_NameOld", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(void), parameterTypes: new[] { typeof(string) }); var ilGeneratorForSetter = setterBuilder.GetILGenerator(); ilGeneratorForSetter.Emit(OpCodes.Ldarg_0); ilGeneratorForSetter.Emit(OpCodes.Ldarg_1); ilGeneratorForSetter.Emit(OpCodes.Stfld, nameNewfieldBuilder); ilGeneratorForSetter.Emit(OpCodes.Ret); propertyBuilder.SetSetMethod(setterBuilder); propertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(DeprecatedAttribute).GetConstructor(new[] { typeof(string) }), new[] { "NameNew" })); } ); var clone = ResolveDynamicAssembliesWhilePerformingAction( () => Deserialise(serialisedData, destinationType), destinationType ); Assert.Equal("Test", destinationType.GetField("NameNew").GetValue(clone)); }
public void StaticDataIsNotSerialised() { // Only instance fields will be serialised, which means that any fields / properties will be unaffected by the deserialisation process. To illustrate this.. // - Create something to clone that has a property and set that property to a known value var source = new ClassWithStaticProperty(); ClassWithStaticProperty.Count = 1; // - Serialise that data (if fields were going to be serialised then the value of the field would be captured here) var serialisedData = BinarySerialisation.Serialise(source, new ISerialisationTypeConverter[0], _referenceReuseStrategy); // - Change the property to a different value ClassWithStaticProperty.Count = 2; // - Deserialise.. if this were to read a value for the property from the serialised data and set it then the property value would revert back to // the value that it had when it was serialised var clone = BinarySerialisation.Deserialise <ClassWithStaticProperty>(serialisedData); // - Confirm that the property was NOT reverted back to the value that it had when the data was serialised Assert.Equal(2, ClassWithStaticProperty.Count); }
public static void DeserialisationWillFailIfAnyFieldsHaveNoData() { var sourceType = ConstructType(GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", new Tuple <string, Type> [0]); var instance = Activator.CreateInstance(sourceType); var serialisedData = BinarySerialisation.Serialise(instance); var destinationType = ConstructType( GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", new[] { Tuple.Create("Id", typeof(int)) } ); Assert.Throws <FieldNotPresentInSerialisedDataException>(() => ResolveDynamicAssembliesWhilePerformingAction( () => Deserialise(serialisedData, destinationType), destinationType ) ); }
public static void DeprecatedPropertyShouldBeSerialisedAsIfItIsAnAutoProperty() { // In a real world scenario, the "future type" would have other properties and the [Deprecated] one(s) would have values computed from them.. for the interests of this // unit test, the future type (typeWithDeprecatedProperty) will ONLY have a [Deprecated] property that has a "computed value" (ie. a getter that always returns 123) const string idPropertyName = "Id"; const int hardCodedIdValueForDeprecatedProperty = 123; var typeWithDeprecatedProperty = ConstructType( GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", fields: new Tuple <string, Type> [0], optionalFinisher: typeBuilder => { var propertyBuilder = typeBuilder.DefineProperty(idPropertyName, PropertyAttributes.None, typeof(int), parameterTypes: Type.EmptyTypes); propertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(DeprecatedAttribute).GetConstructor(new[] { typeof(string) }), new object[] { null })); var getterBuilder = typeBuilder.DefineMethod("get_" + idPropertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(int), parameterTypes: Type.EmptyTypes); var ilGenerator = getterBuilder.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldc_I4, hardCodedIdValueForDeprecatedProperty); ilGenerator.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(getterBuilder); } ); var instance = Activator.CreateInstance(typeWithDeprecatedProperty); var serialisedData = BinarySerialisation.Serialise(instance); // For the sake of this test, the type that will be deserialised to only needs to have a backing field for an auto property (the field that we're expecting to // get set) and so that's all that is being configured. It would be closer to a real use case if there was a property with a getter that used this backing field // but it wouldn't be used by this test and so it doesn't need to be created. var idBackingFieldName = BackingFieldHelpers.GetBackingFieldName(idPropertyName); var typeThatStillHasThePropertyOnIt = ConstructType( GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "MyClass", new[] { Tuple.Create(idBackingFieldName, typeof(int)) } ); var deserialised = ResolveDynamicAssembliesWhilePerformingAction( () => Deserialise(serialisedData, typeThatStillHasThePropertyOnIt), typeThatStillHasThePropertyOnIt ); var idBackingFieldOnDestination = typeThatStillHasThePropertyOnIt.GetField(idBackingFieldName); Assert.Equal(123, idBackingFieldOnDestination.GetValue(deserialised)); }
private static void AssertCanGenerateCorrectMemberSetter(object source) { var memberSetterDetails = TryToGenerateMemberSetter(source.GetType()); Assert.NotNull(memberSetterDetails); byte[] serialised; using (var stream = new MemoryStream()) { // In an ideal world, this wouldn't be so tied to implementation details of the Serialiser and BinarySerialisationWriter classes but I can't // immediately think of a way to fully test this otherwise - one option would be to skip the writing of the FieldNamePreLoad data and to skip // the ObjectStart and ObjectEnd calls and to then read the data back out via a BinarySerialisationReader and manually compare the field and // property names to expected values but I wanted to offload that sort of comparison work to a library like CompareNetObjects! // - "Optimisied member setters" are not usually generated until after an instance of a type has been serialised without, in which case all // field names will have appeared in the serialised data at least once, which is important because the member setters will write out Name // Reference IDs for fields and the reader needs to know what strings those IDs map on to. Since this code won't be serialising an instance // of each type before using the member setter, FieldNamePreLoad data can be injected into the start of the serialised data and then the // reader will be able to refer to use that to map IDs to strings. foreach (var fieldName in memberSetterDetails.FieldsSet) { var fieldNameBytes = new[] { (byte)BinarySerialisationDataType.FieldNamePreLoad }.Concat(fieldName.AsStringAndReferenceID).ToArray(); stream.Write(fieldNameBytes, 0, fieldNameBytes.Length); } var writer = new BinarySerialisationWriter(stream); writer.ObjectStart(source.GetType()); memberSetterDetails.GetCompiledMemberSetter()(source, writer); writer.ObjectEnd(); serialised = stream.ToArray(); } var clone = BinarySerialisation.Deserialise <object>(serialised); if (!ObjectComparer.AreEqual(source, clone, out var differenceSummaryIfNotEqual)) { throw new Exception("Clone failed: " + differenceSummaryIfNotEqual); } }
public EntitiesForFastestTreeBinarySerialisation.Product[] DanSerialiserDeserialise_FastestTreeBinarySerialisationWithHints() { return(BinarySerialisation.Deserialise <EntitiesForFastestTreeBinarySerialisation.Product[]>(_danSerialiserSerialisedDataFastButSpeedyWithHints)); }
public Product[] DanSerialiserDeserialise_FastestTreeBinarySerialisation() => BinarySerialisation.Deserialise <Product[]>(_danSerialiserSerialisedDataFastButSpeedy);
public Product[] DanSerialiserDeserialise_OptimisedForWideCircularReferences() => BinarySerialisation.Deserialise <Product[]>(_danSerialiserSerialisedDataOptimisedForWideCircularReferences);
public Product[] DanSerialiserDeserialise() => BinarySerialisation.Deserialise <Product[]>(_danSerialiserSerialisedData);
public byte[] DanSerialiserSerialise_OptimisedForWideCircularReferences() => BinarySerialisation.Serialise(_products, optimiseForWideCircularReference: true);
public byte[] DanSerialiserSerialise() => BinarySerialisation.Serialise(_products, optimiseForWideCircularReference: false);
public static void DeprecatedPropertyIsWrittenAgainstOldPropertyNameAsWellAsNew() { // Define two versions of the same class where the V1 looks like this: // // public class SomethingWithName // { // public string NameOld { get; set; } // } // // .. and the V2 looks like this: // // public class SomethingWithName // { // public string NameNew { get; set; } // // [Deprecated] // public string NameOld { get { return NameNew; } } // } // // If an instance of V2 entity is serialised and then deserialised into the V1 entity type then the "NameOld" field of the V1 entity instance should be populated (in order for // this to work, the member-setter-generation logic needs to consider fields and properties and the attributes that they do or do not have) var entityTypeV1 = ConstructType( GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "SomethingWithName", fields: new Tuple <string, Type> [0], optionalFinisher: typeBuilder => { // Backing field for "NameOld" var nameOldfieldBuilder = typeBuilder.DefineField(BackingFieldHelpers.GetBackingFieldName("NameOld"), typeof(string), FieldAttributes.Private); // Property for "NameOld" var nameOldPropertyBuilder = typeBuilder.DefineProperty("NameOld", PropertyAttributes.None, typeof(string), parameterTypes: Type.EmptyTypes); var nameOldGetterBuilder = typeBuilder.DefineMethod("get_NameOld", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(string), parameterTypes: Type.EmptyTypes); var ilGeneratorForNameOldGetter = nameOldGetterBuilder.GetILGenerator(); ilGeneratorForNameOldGetter.Emit(OpCodes.Ldarg_0); ilGeneratorForNameOldGetter.Emit(OpCodes.Ldfld, nameOldfieldBuilder); ilGeneratorForNameOldGetter.Emit(OpCodes.Ret); nameOldPropertyBuilder.SetGetMethod(nameOldGetterBuilder); var nameOldSetterBuilder = typeBuilder.DefineMethod("set_NameOld", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(void), parameterTypes: new[] { typeof(string) }); var ilGeneratorForNameOldSetter = nameOldSetterBuilder.GetILGenerator(); ilGeneratorForNameOldSetter.Emit(OpCodes.Ldarg_0); ilGeneratorForNameOldSetter.Emit(OpCodes.Ldarg_1); ilGeneratorForNameOldSetter.Emit(OpCodes.Stfld, nameOldfieldBuilder); ilGeneratorForNameOldSetter.Emit(OpCodes.Ret); nameOldPropertyBuilder.SetSetMethod(nameOldSetterBuilder); } ); var entityTypeV2 = ConstructType( GetModuleBuilder("DynamicAssemblyFor" + GetMyName(), new Version(1, 0)), "SomethingWithName", fields: new Tuple <string, Type> [0], optionalFinisher: typeBuilder => { // Backing field for "NameNew" var nameNewfieldBuilder = typeBuilder.DefineField(BackingFieldHelpers.GetBackingFieldName("NameNew"), typeof(string), FieldAttributes.Private); // Property for "NameNew" var nameNewPropertyBuilder = typeBuilder.DefineProperty("NameNew", PropertyAttributes.None, typeof(string), parameterTypes: Type.EmptyTypes); var nameNewGetterBuilder = typeBuilder.DefineMethod("get_NameNew", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(string), parameterTypes: Type.EmptyTypes); var ilGeneratorForNameNewGetter = nameNewGetterBuilder.GetILGenerator(); ilGeneratorForNameNewGetter.Emit(OpCodes.Ldarg_0); ilGeneratorForNameNewGetter.Emit(OpCodes.Ldfld, nameNewfieldBuilder); ilGeneratorForNameNewGetter.Emit(OpCodes.Ret); nameNewPropertyBuilder.SetGetMethod(nameNewGetterBuilder); var nameNewSetterBuilder = typeBuilder.DefineMethod("set_NameNew", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(void), parameterTypes: new[] { typeof(string) }); var ilGeneratorForNameNewSetter = nameNewSetterBuilder.GetILGenerator(); ilGeneratorForNameNewSetter.Emit(OpCodes.Ldarg_0); ilGeneratorForNameNewSetter.Emit(OpCodes.Ldarg_1); ilGeneratorForNameNewSetter.Emit(OpCodes.Stfld, nameNewfieldBuilder); ilGeneratorForNameNewSetter.Emit(OpCodes.Ret); nameNewPropertyBuilder.SetSetMethod(nameNewSetterBuilder); // Property for "NameOld" that has [Deprecated] attribute and whose getter access "NameNew" var nameOldPropertyBuilder = typeBuilder.DefineProperty("NameOld", PropertyAttributes.None, typeof(string), parameterTypes: Type.EmptyTypes); var nameOldGetterBuilder = typeBuilder.DefineMethod("get_NameOld", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, typeof(string), parameterTypes: Type.EmptyTypes); var ilGeneratorForNameOldGetter = nameOldGetterBuilder.GetILGenerator(); ilGeneratorForNameOldGetter.Emit(OpCodes.Ldarg_0); ilGeneratorForNameOldGetter.Emit(OpCodes.Call, nameNewGetterBuilder); ilGeneratorForNameOldGetter.Emit(OpCodes.Ret); nameOldPropertyBuilder.SetGetMethod(nameOldGetterBuilder); nameOldPropertyBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(DeprecatedAttribute).GetConstructor(new[] { typeof(string) }), new object[] { null })); } ); // Create an instance of the V2 entity var source = Activator.CreateInstance(entityTypeV2); entityTypeV2.GetProperty("NameNew").SetValue(source, "abc"); // Try to create a member setter for it - this should work since it only has string fields and properties var memberSetterDetails = GetMemberSetterAvailability( entityTypeV2, DefaultTypeAnalyser.Instance, valueWriterRetriever: t => null // No complex nested member setter madness required, so provide a valueWriterRetriever delegate that always returns null ) .MemberSetterDetailsIfSuccessful; Assert.NotNull(memberSetterDetails); // Serialise this v2 entity instance byte[] serialised; using (var stream = new MemoryStream()) { foreach (var fieldName in memberSetterDetails.FieldsSet) { var fieldNameBytes = new[] { (byte)BinarySerialisationDataType.FieldNamePreLoad }.Concat(fieldName.AsStringAndReferenceID).ToArray(); stream.Write(fieldNameBytes, 0, fieldNameBytes.Length); } var writer = new BinarySerialisationWriter(stream); writer.ObjectStart(source.GetType()); memberSetterDetails.GetCompiledMemberSetter()(source, writer); writer.ObjectEnd(); serialised = stream.ToArray(); } // Ensure that it works deserialising back to an older version of the type var deserialisedAsV1 = ResolveDynamicAssembliesWhilePerformingAction( () => BinarySerialisation.Deserialise <object>(serialised), entityTypeV1 ); Assert.NotNull(deserialisedAsV1); Assert.IsType(entityTypeV1, deserialisedAsV1); Assert.Equal("abc", deserialisedAsV1.GetType().GetProperty("NameOld").GetValue(deserialisedAsV1)); }