// 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()); }