Example #1
0
        // Registers surrogates with all of the operations on the specified contract. These surrogates
        // take care of representing entities in the right shape based on TypeDescriptor extensions.
        private static void RegisterSurrogates(ContractDescription contractDesc, DomainServiceDescription domainServiceDescription)
        {
            // Cache the list of entity types and surrogate types.
            HashSet <Type> exposedTypes = new HashSet <Type>();
            Dictionary <Type, Tuple <Type, Func <object, object> > > exposedTypeToSurrogateMap = new Dictionary <Type, Tuple <Type, Func <object, object> > >();
            HashSet <Type> surrogateTypes = new HashSet <Type>();

            foreach (Type entityType in domainServiceDescription.EntityTypes)
            {
                exposedTypes.Add(entityType);
            }

            // Because complex types and entities cannot share an inheritance relationship, we can add them to the same surrogate set.
            foreach (Type complexType in domainServiceDescription.ComplexTypes)
            {
                exposedTypes.Add(complexType);
            }

            foreach (Type exposedType in exposedTypes)
            {
                Type surrogateType = DataContractSurrogateGenerator.GetSurrogateType(exposedTypes, exposedType);
                Func <object, object> surrogateFactory             = (Func <object, object>)DynamicMethodUtility.GetFactoryMethod(surrogateType.GetConstructor(new Type[] { exposedType }), typeof(Func <object, object>));
                Tuple <Type, Func <object, object> > surrogateInfo = new Tuple <Type, Func <object, object> >(surrogateType, surrogateFactory);
                exposedTypeToSurrogateMap.Add(exposedType, surrogateInfo);
                surrogateTypes.Add(surrogateType);
            }

            DomainServiceSerializationSurrogate surrogate = new DomainServiceSerializationSurrogate(domainServiceDescription, exposedTypeToSurrogateMap, surrogateTypes);

            // Register our serialization surrogate with the WSDL exporter.
            DomainServiceWsdlExportExtension wsdlExportExtension = contractDesc.Behaviors.Find <DomainServiceWsdlExportExtension>();

            if (wsdlExportExtension == null)
            {
                wsdlExportExtension = new DomainServiceWsdlExportExtension(surrogate);
                contractDesc.Behaviors.Add(wsdlExportExtension);
            }

            // Register our serialization surrogate with the actual invoke operations.
            foreach (OperationDescription op in contractDesc.Operations)
            {
                foreach (Type surrogateType in surrogateTypes)
                {
                    op.KnownTypes.Add(surrogateType);
                }

                DataContractSerializerOperationBehavior dataContractBehavior = op.Behaviors.Find <DataContractSerializerOperationBehavior>();
                if (dataContractBehavior == null)
                {
                    dataContractBehavior = new DataContractSerializerOperationBehavior(op);
                    op.Behaviors.Add(dataContractBehavior);
                }
                dataContractBehavior.DataContractSurrogate = surrogate;
            }
        }
        private static void EmitProperties(TypeBuilder typeBuilder, Type type, Type parentSurrogateType, FieldInfo wrapperField, Lazy <ILGenerator> typeInitializerGenerator)
        {
            bool hasParent = (parentSurrogateType != typeof(object));

            // Create fields for each of the properties.
            foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(type))
            {
                // Skip properties inherited from a base class.
                // We allow "holes" in the visible entity hierarchy (i.e. some entity types are not exposed
                // via the DomainService and hence do not appear in the list of known entity types).
                // Properties appearing in unexposed entities are lifted to the first visible subclass.
                // So if our parent surrogate does not declare a property, it means we have discovered
                // a hole in the visible hierarchy and must lift that property.
                if (hasParent && pd.ComponentType != type && parentSurrogateType.GetProperty(pd.Name) != null)
                {
                    continue;
                }

                if (SerializationUtility.IsSerializableDataMember(pd))
                {
                    DataContractSurrogateGenerator.EmitProperty(typeBuilder, wrapperField, typeInitializerGenerator, pd, pd.Name);
                }
            }
        }
        // Emit a surrogate property for a CLR property.
        // The surrogate code we'll generate will look like this:
        // public <PropertyType> <PropertyName> {
        //     get {
        //         // For normal types.
        //         return _$wrapper.<PropertyName>;
        //
        //         // For Binary.
        //         Binary value = _$wrapper.<PropertyName>;
        //         if (value == null) {
        //             return null;
        //         }
        //         return value.ToArray();
        //     }
        //     set {
        //         if (value == null) {
        //             return;
        //         }
        //
        //         // For normal types.
        //         _$wrapper.<PropertyName> = value;
        //
        //         // For Binary.
        //         Binary valueToStore;
        //         if (value == null) {
        //             valueToStore = null;
        //         }
        //         else {
        //             valueToStore = new Binary(value);
        //         }
        //         _$wrapper.<PropertyName> = valueToStore;
        //     }
        // }
        private static void EmitClrProperty(TypeBuilder typeBuilder, FieldInfo wrapperField, PropertyDescriptor pd, PropertyInfo pi, string name)
        {
            Type            propertyType    = SerializationUtility.GetClientType(pi.PropertyType);
            PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.None, propertyType, null);

            CustomAttributeBuilder dataMemberAtt = DataContractSurrogateGenerator.GetDataMemberAttributeBuilder(
                pd.Attributes.OfType <DataMemberAttribute>().FirstOrDefault());

            propertyBuilder.SetCustomAttribute(dataMemberAtt);

            // get {
            //     return ((Entity)$wrapper).Property;
            // }
            MethodBuilder getPropertyMethodBuilder = typeBuilder.DefineMethod("get_" + name, MethodAttributes.Public, propertyType, Type.EmptyTypes);
            ILGenerator   generator = getPropertyMethodBuilder.GetILGenerator();

            // Push the wrapper object onto the stack.
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldfld, wrapperField);
            generator.Emit(OpCodes.Castclass, pi.DeclaringType);

            // Call the property getter.
            generator.Emit(OpCodes.Callvirt, pi.GetGetMethod());

            // Deal with client-server type conversions.
            if (propertyType != pi.PropertyType)
            {
                EmitToClientConversion(generator, pi.PropertyType, propertyType);
            }

            generator.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getPropertyMethodBuilder);

            MethodBuilder setPropertyMethodBuilder = typeBuilder.DefineMethod("set_" + name, MethodAttributes.Public, null, new Type[] { propertyType });

            generator = setPropertyMethodBuilder.GetILGenerator();

            Label returnLabel = generator.DefineLabel();

            // Data members require a getter and setter. However, if the real property is read-only, make sure
            // our surrogate property setter is a no-op.
            MethodInfo setMethod = pi.GetSetMethod();

            if (setMethod != null && setMethod.IsPublic)
            {
                // NOTE: We don't ever set null values, because a property may be required. For
                //       original objects however it's possible that required properties are not
                //       roundtripped, as they may not have RoundtripOriginalAttribute.
                // set {
                //     if (value != null) {
                //         _$wrapper.Property = value;
                //     }
                // }

                // If the value is null, return.
                if (!propertyType.IsValueType)
                {
                    generator.Emit(OpCodes.Ldarg_1);
                    EmitBranchIfNull(generator, propertyType, returnLabel);
                }
                else if (TypeUtility.IsNullableType(propertyType))
                {
                    generator.Emit(OpCodes.Ldarga_S, 1);
                    EmitBranchIfNull(generator, propertyType, returnLabel);
                }

                // Push the wrapper object onto the stack.
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldfld, wrapperField);
                generator.Emit(OpCodes.Castclass, pi.DeclaringType);

                // Push the value onto the stack.
                generator.Emit(OpCodes.Ldarg_1);

                // Deal with client-server type conversions.
                if (propertyType != pi.PropertyType)
                {
                    EmitToServerConversion(generator, propertyType, pi.PropertyType);
                }

                // Call the property setter.
                generator.Emit(OpCodes.Callvirt, setMethod);
            }

            generator.MarkLabel(returnLabel);
            generator.Emit(OpCodes.Ret);
            propertyBuilder.SetSetMethod(setPropertyMethodBuilder);
        }
        // Emit a surrogate property for a virtual property (a property that doesn't exist on the physical CLR type).
        // The PropertyDescriptor for each virtual property is initialized in the type initializer of a surrogate type.
        // The surrogate code we'll generate will look like this:
        // public <PropertyType> <PropertyName> {
        //     get {
        //         // For reference types.
        //         return (<PropertyType>)$<PropertyName>.GetValue(_$wrapper);
        //
        //         // For value types.
        //         object value = $<PropertyName>.GetValue(_$wrapper);
        //         if (value == null) {
        //             return default(value);
        //         }
        //         return (<PropertyType>)value;
        //
        //         // For Binary.
        //         Binary value = (Binary)$<PropertyName>.GetValue(_$wrapper);
        //         if (value == null) {
        //             return null;
        //         }
        //         return value.ToArray();
        //     }
        //     set {
        //         if (value == null) {
        //             return;
        //         }
        //
        //         // For normal types.
        //         $<PropertyName>.SetValue(_$wrapper, value);
        //
        //         // For value types.
        //         $<PropertyName>.SetValue(_$wrapper, (object)value);
        //
        //         // For Binary.
        //         Binary valueToStore;
        //         if (value == null) {
        //             valueToStore = null;
        //         }
        //         else {
        //             valueToStore = new Binary(value);
        //         }
        //         $<PropertyName>.SetValue(_$wrapper, valueToStore);
        //     }
        // }
        private static void EmitAttachedProperty(TypeBuilder typeBuilder, FieldInfo wrapperField, Lazy <ILGenerator> typeInitializerFactory, PropertyDescriptor pd, string name)
        {
            // private static PropertyDescriptor $property;
            FieldBuilder propertyDescFieldBuilder = typeBuilder.DefineField("$" + name, typeof(PropertyDescriptor), FieldAttributes.Private | FieldAttributes.Static);

            EmitPropertyInitializer(propertyDescFieldBuilder, typeInitializerFactory, name);

            Type            propertyType    = SerializationUtility.GetClientType(pd.PropertyType);
            PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(name, PropertyAttributes.None, propertyType, null);

            CustomAttributeBuilder dataMemberAtt = DataContractSurrogateGenerator.GetDataMemberAttributeBuilder(
                pd.Attributes[typeof(DataMemberAttribute)] as DataMemberAttribute);

            propertyBuilder.SetCustomAttribute(dataMemberAtt);

            // get {
            //     return $property.GetValue(_$wrapper);
            // }
            MethodBuilder getPropertyMethodBuilder = typeBuilder.DefineMethod("get_" + name, MethodAttributes.Public, propertyType, Type.EmptyTypes);
            ILGenerator   generator = getPropertyMethodBuilder.GetILGenerator();

            // Get the PropertyDescriptor.
            generator.Emit(OpCodes.Ldsfld, propertyDescFieldBuilder);

            // Push the wrapper object onto the stack. We'll use it as an argument for our
            // call to GetValue later on.
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldfld, wrapperField);

            // PropertyDescriptor.GetValue(_$wrapper).
            generator.Emit(OpCodes.Callvirt, typeof(PropertyDescriptor).GetMethod("GetValue"));

            // Unbox/cast.
            DynamicMethodUtility.EmitFromObjectConversion(generator, pd.PropertyType);

            // Deal with client-server type conversions.
            if (propertyType != pd.PropertyType)
            {
                EmitToClientConversion(generator, pd.PropertyType, propertyType);
            }

            generator.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getPropertyMethodBuilder);

            MethodBuilder setPropertyMethodBuilder = typeBuilder.DefineMethod("set_" + name, MethodAttributes.Public, null, new Type[] { propertyType });

            generator = setPropertyMethodBuilder.GetILGenerator();

            Label returnLabel = generator.DefineLabel();

            // Data members require a getter and setter. However, if the real property is read-only, make sure
            // our surrogate property setter is a no-op.
            if (!pd.IsReadOnly)
            {
                // NOTE: We don't ever set null values, because a property may be required. For
                //       original objects however it's possible that required properties are not
                //       roundtripped, as they may not have RoundtripOriginalAttribute.
                // set {
                //     if (value != null) {
                //         $property.SetValue(_$wrapper, value);
                //     }
                // }

                // If the value is null, return.
                if (!propertyType.IsValueType)
                {
                    generator.Emit(OpCodes.Ldarg_1);
                    EmitBranchIfNull(generator, propertyType, returnLabel);
                }
                else if (TypeUtility.IsNullableType(propertyType))
                {
                    generator.Emit(OpCodes.Ldarga_S, 1);
                    EmitBranchIfNull(generator, propertyType, returnLabel);
                }

                // Get the PropertyDescriptor.
                generator.Emit(OpCodes.Ldsfld, propertyDescFieldBuilder);

                // Push the wrapper object onto the stack. We'll use it as an argument for our
                // call to SetValue later on.
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldfld, wrapperField);

                // Push the value onto the stack. We'll use it as the 2nd argument for
                // our call to SetValue.
                generator.Emit(OpCodes.Ldarg_1);

                // Deal with client-server type conversions.
                if (propertyType != pd.PropertyType)
                {
                    EmitToServerConversion(generator, propertyType, pd.PropertyType);
                }

                // Box value types.
                DynamicMethodUtility.EmitToObjectConversion(generator, pd.PropertyType);

                // PropertyDescriptor.SetValue(_$wrapper, value).
                generator.Emit(OpCodes.Callvirt, typeof(PropertyDescriptor).GetMethod("SetValue"));
            }

            generator.MarkLabel(returnLabel);
            generator.Emit(OpCodes.Ret);
            propertyBuilder.SetSetMethod(setPropertyMethodBuilder);
        }
        private static Type CreateSurrogateType(HashSet <Type> knownExposedTypes, Type type)
        {
            CustomAttributeBuilder dataContractAttBuilder = DataContractSurrogateGenerator.GetDataContractAttributeBuilder(type);

            // Emit a dynamic type.
            TypeAttributes typeAttributes = TypeAttributes.Public;

            if (type.IsSealed)
            {
                typeAttributes |= TypeAttributes.Sealed;
            }

            TypeBuilder typeBuilder = DataContractSurrogateGenerator.moduleBuilder.DefineType(type.FullName, typeAttributes);

            if (dataContractAttBuilder != null)
            {
                typeBuilder.SetCustomAttribute(dataContractAttBuilder);
            }

            // Attach a [SecuritySafeCritical] to the surrogate type to permit its setters to deal with
            // types that may be SafeCritical or Critical
            // If the AppDomain is running in medium trust, this attribute won't have any effect.
            CustomAttributeBuilder safeCriticalAttrBuilder = new CustomAttributeBuilder(
                typeof(SecuritySafeCriticalAttribute).GetConstructor(Type.EmptyTypes),
                new object[0]);

            typeBuilder.SetCustomAttribute(safeCriticalAttrBuilder);

            Type actualParentType;
            Type parentSurrogateType = DataContractSurrogateGenerator.GetSurrogateType(knownExposedTypes, type.BaseType);

            if (parentSurrogateType != null)
            {
                // Figure out what the actual parent of the exposed type is.
                actualParentType = type.BaseType;
                while (actualParentType != typeof(object))
                {
                    if (knownExposedTypes.Contains(actualParentType))
                    {
                        break;
                    }

                    actualParentType = actualParentType.BaseType;
                }

                typeBuilder.SetParent(parentSurrogateType);
            }
            else
            {
                parentSurrogateType = typeof(object);
                actualParentType    = typeof(object);
            }

            FieldInfo wrapperField = EmitICloneableImplementation(typeBuilder, type, parentSurrogateType);

            EmitConstructorsAndProperties(typeBuilder, type, parentSurrogateType, actualParentType, wrapperField);
            EmitOnDeserializingMethod(typeBuilder, type, wrapperField);
            EmitContractNamespaceAttributes(type);

            return(typeBuilder.CreateType());
        }