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