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);
        }
Exemple #6
0
        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);
            }
        }
Exemple #8
0
        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));
        }
Exemple #15
0
        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));
        }