DataContractSerializerOperationGenerator.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Description
  5. {
  6. using System.CodeDom;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Globalization;
  10. using System.Runtime;
  11. using System.Runtime.Serialization;
  12. using System.ServiceModel.Channels;
  13. using System.ServiceModel.Dispatcher;
  14. class DataContractSerializerOperationGenerator : IOperationBehavior, IOperationContractGenerationExtension
  15. {
  16. Dictionary<OperationDescription, DataContractFormatAttribute> operationAttributes = new Dictionary<OperationDescription, DataContractFormatAttribute>();
  17. OperationGenerator operationGenerator;
  18. Dictionary<MessagePartDescription, ICollection<CodeTypeReference>> knownTypes;
  19. Dictionary<MessagePartDescription, bool> isNonNillableReferenceTypes;
  20. CodeCompileUnit codeCompileUnit;
  21. public DataContractSerializerOperationGenerator() : this(new CodeCompileUnit()) { }
  22. public DataContractSerializerOperationGenerator(CodeCompileUnit codeCompileUnit)
  23. {
  24. this.codeCompileUnit = codeCompileUnit;
  25. this.operationGenerator = new OperationGenerator();
  26. }
  27. internal void Add(MessagePartDescription part, CodeTypeReference typeReference, ICollection<CodeTypeReference> knownTypeReferences, bool isNonNillableReferenceType)
  28. {
  29. OperationGenerator.ParameterTypes.Add(part, typeReference);
  30. if (knownTypeReferences != null)
  31. KnownTypes.Add(part, knownTypeReferences);
  32. if (isNonNillableReferenceType)
  33. {
  34. if (isNonNillableReferenceTypes == null)
  35. isNonNillableReferenceTypes = new Dictionary<MessagePartDescription, bool>();
  36. isNonNillableReferenceTypes.Add(part, isNonNillableReferenceType);
  37. }
  38. }
  39. internal OperationGenerator OperationGenerator
  40. {
  41. get { return this.operationGenerator; }
  42. }
  43. internal Dictionary<OperationDescription, DataContractFormatAttribute> OperationAttributes
  44. {
  45. get { return operationAttributes; }
  46. }
  47. internal Dictionary<MessagePartDescription, ICollection<CodeTypeReference>> KnownTypes
  48. {
  49. get
  50. {
  51. if (this.knownTypes == null)
  52. this.knownTypes = new Dictionary<MessagePartDescription, ICollection<CodeTypeReference>>();
  53. return this.knownTypes;
  54. }
  55. }
  56. void IOperationBehavior.Validate(OperationDescription description)
  57. {
  58. }
  59. void IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) { }
  60. void IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy) { }
  61. void IOperationBehavior.AddBindingParameters(OperationDescription description, BindingParameterCollection parameters) { }
  62. // Assumption: gets called exactly once per operation
  63. void IOperationContractGenerationExtension.GenerateOperation(OperationContractGenerationContext context)
  64. {
  65. DataContractSerializerOperationBehavior DataContractSerializerOperationBehavior = context.Operation.Behaviors.Find<DataContractSerializerOperationBehavior>() as DataContractSerializerOperationBehavior;
  66. DataContractFormatAttribute dataContractFormatAttribute = (DataContractSerializerOperationBehavior == null) ? new DataContractFormatAttribute() : DataContractSerializerOperationBehavior.DataContractFormatAttribute;
  67. OperationFormatStyle style = dataContractFormatAttribute.Style;
  68. operationGenerator.GenerateOperation(context, ref style, false/*isEncoded*/, new WrappedBodyTypeGenerator(this, context), knownTypes);
  69. dataContractFormatAttribute.Style = style;
  70. if (dataContractFormatAttribute.Style != TypeLoader.DefaultDataContractFormatAttribute.Style)
  71. context.SyncMethod.CustomAttributes.Add(OperationGenerator.GenerateAttributeDeclaration(context.Contract.ServiceContractGenerator, dataContractFormatAttribute));
  72. if (knownTypes != null)
  73. {
  74. Dictionary<CodeTypeReference, object> operationKnownTypes = new Dictionary<CodeTypeReference, object>(new CodeTypeReferenceComparer());
  75. foreach (MessageDescription message in context.Operation.Messages)
  76. {
  77. foreach (MessagePartDescription part in message.Body.Parts)
  78. AddKnownTypesForPart(context, part, operationKnownTypes);
  79. foreach (MessageHeaderDescription header in message.Headers)
  80. AddKnownTypesForPart(context, header, operationKnownTypes);
  81. if (OperationFormatter.IsValidReturnValue(message.Body.ReturnValue))
  82. AddKnownTypesForPart(context, message.Body.ReturnValue, operationKnownTypes);
  83. }
  84. }
  85. UpdateTargetCompileUnit(context, this.codeCompileUnit);
  86. }
  87. void AddKnownTypesForPart(OperationContractGenerationContext context, MessagePartDescription part, Dictionary<CodeTypeReference, object> operationKnownTypes)
  88. {
  89. ICollection<CodeTypeReference> knownTypesForPart;
  90. if (knownTypes.TryGetValue(part, out knownTypesForPart))
  91. {
  92. foreach (CodeTypeReference knownTypeReference in knownTypesForPart)
  93. {
  94. object value;
  95. if (!operationKnownTypes.TryGetValue(knownTypeReference, out value))
  96. {
  97. operationKnownTypes.Add(knownTypeReference, null);
  98. CodeAttributeDeclaration knownTypeAttribute = new CodeAttributeDeclaration(typeof(ServiceKnownTypeAttribute).FullName);
  99. knownTypeAttribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(knownTypeReference)));
  100. context.SyncMethod.CustomAttributes.Add(knownTypeAttribute);
  101. }
  102. }
  103. }
  104. }
  105. internal static void UpdateTargetCompileUnit(OperationContractGenerationContext context, CodeCompileUnit codeCompileUnit)
  106. {
  107. CodeCompileUnit targetCompileUnit = context.ServiceContractGenerator.TargetCompileUnit;
  108. if (!Object.ReferenceEquals(targetCompileUnit, codeCompileUnit))
  109. {
  110. foreach (CodeNamespace codeNamespace in codeCompileUnit.Namespaces)
  111. if (!targetCompileUnit.Namespaces.Contains(codeNamespace))
  112. targetCompileUnit.Namespaces.Add(codeNamespace);
  113. foreach (string referencedAssembly in codeCompileUnit.ReferencedAssemblies)
  114. if (!targetCompileUnit.ReferencedAssemblies.Contains(referencedAssembly))
  115. targetCompileUnit.ReferencedAssemblies.Add(referencedAssembly);
  116. foreach (CodeAttributeDeclaration assemblyCustomAttribute in codeCompileUnit.AssemblyCustomAttributes)
  117. if (!targetCompileUnit.AssemblyCustomAttributes.Contains(assemblyCustomAttribute))
  118. targetCompileUnit.AssemblyCustomAttributes.Add(assemblyCustomAttribute);
  119. foreach (CodeDirective startDirective in codeCompileUnit.StartDirectives)
  120. if (!targetCompileUnit.StartDirectives.Contains(startDirective))
  121. targetCompileUnit.StartDirectives.Add(startDirective);
  122. foreach (CodeDirective endDirective in codeCompileUnit.EndDirectives)
  123. if (!targetCompileUnit.EndDirectives.Contains(endDirective))
  124. targetCompileUnit.EndDirectives.Add(endDirective);
  125. foreach (DictionaryEntry userData in codeCompileUnit.UserData)
  126. targetCompileUnit.UserData[userData.Key] = userData.Value;
  127. }
  128. }
  129. internal class WrappedBodyTypeGenerator : IWrappedBodyTypeGenerator
  130. {
  131. static CodeTypeReference dataContractAttributeTypeRef = new CodeTypeReference(typeof(DataContractAttribute));
  132. int memberCount;
  133. OperationContractGenerationContext context;
  134. DataContractSerializerOperationGenerator dataContractSerializerOperationGenerator;
  135. public void ValidateForParameterMode(OperationDescription operation)
  136. {
  137. if (dataContractSerializerOperationGenerator.isNonNillableReferenceTypes == null)
  138. return;
  139. foreach (MessageDescription messageDescription in operation.Messages)
  140. {
  141. if (messageDescription.Body != null)
  142. {
  143. if (messageDescription.Body.ReturnValue != null)
  144. ValidateForParameterMode(messageDescription.Body.ReturnValue);
  145. foreach (MessagePartDescription bodyPart in messageDescription.Body.Parts)
  146. {
  147. ValidateForParameterMode(bodyPart);
  148. }
  149. }
  150. }
  151. }
  152. void ValidateForParameterMode(MessagePartDescription part)
  153. {
  154. if (dataContractSerializerOperationGenerator.isNonNillableReferenceTypes.ContainsKey(part))
  155. {
  156. ParameterModeException parameterModeException = new ParameterModeException(SR.GetString(SR.SFxCannotImportAsParameters_ElementIsNotNillable, part.Name, part.Namespace));
  157. parameterModeException.MessageContractType = MessageContractType.BareMessageContract;
  158. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(parameterModeException);
  159. }
  160. }
  161. public WrappedBodyTypeGenerator(DataContractSerializerOperationGenerator dataContractSerializerOperationGenerator, OperationContractGenerationContext context)
  162. {
  163. this.context = context;
  164. this.dataContractSerializerOperationGenerator = dataContractSerializerOperationGenerator;
  165. }
  166. public void AddMemberAttributes(XmlName messageName, MessagePartDescription part, CodeAttributeDeclarationCollection attributesImported, CodeAttributeDeclarationCollection typeAttributes, CodeAttributeDeclarationCollection fieldAttributes)
  167. {
  168. CodeAttributeDeclaration dataContractAttributeDecl = null;
  169. foreach (CodeAttributeDeclaration attr in typeAttributes)
  170. {
  171. if (attr.AttributeType.BaseType == dataContractAttributeTypeRef.BaseType)
  172. {
  173. dataContractAttributeDecl = attr;
  174. break;
  175. }
  176. }
  177. if (dataContractAttributeDecl == null)
  178. {
  179. Fx.Assert(String.Format(CultureInfo.InvariantCulture, "Cannot find DataContract attribute for {0}", messageName));
  180. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidDataContractException(String.Format(CultureInfo.InvariantCulture, "Cannot find DataContract attribute for {0}", messageName)));
  181. }
  182. bool nsAttrFound = false;
  183. foreach (CodeAttributeArgument attrArg in dataContractAttributeDecl.Arguments)
  184. {
  185. if (attrArg.Name == "Namespace")
  186. {
  187. nsAttrFound = true;
  188. string nsValue = ((CodePrimitiveExpression)attrArg.Value).Value.ToString();
  189. if (nsValue != part.Namespace)
  190. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxWrapperTypeHasMultipleNamespaces, messageName)));
  191. }
  192. }
  193. if (!nsAttrFound)
  194. dataContractAttributeDecl.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(part.Namespace)));
  195. DataMemberAttribute dataMemberAttribute = new DataMemberAttribute();
  196. dataMemberAttribute.Order = memberCount++;
  197. dataMemberAttribute.EmitDefaultValue = !IsNonNillableReferenceType(part);
  198. fieldAttributes.Add(OperationGenerator.GenerateAttributeDeclaration(context.Contract.ServiceContractGenerator, dataMemberAttribute));
  199. }
  200. private bool IsNonNillableReferenceType(MessagePartDescription part)
  201. {
  202. if (dataContractSerializerOperationGenerator.isNonNillableReferenceTypes == null)
  203. return false;
  204. return dataContractSerializerOperationGenerator.isNonNillableReferenceTypes.ContainsKey(part);
  205. }
  206. public void AddTypeAttributes(string messageName, string typeNS, CodeAttributeDeclarationCollection typeAttributes, bool isEncoded)
  207. {
  208. typeAttributes.Add(OperationGenerator.GenerateAttributeDeclaration(context.Contract.ServiceContractGenerator, new DataContractAttribute()));
  209. memberCount = 0;
  210. }
  211. }
  212. class CodeTypeReferenceComparer : IEqualityComparer<CodeTypeReference>
  213. {
  214. public bool Equals(CodeTypeReference x, CodeTypeReference y)
  215. {
  216. if (Object.ReferenceEquals(x, y))
  217. return true;
  218. if (x == null || y == null || x.ArrayRank != y.ArrayRank || x.BaseType != y.BaseType)
  219. return false;
  220. CodeTypeReferenceCollection xTypeArgs = x.TypeArguments;
  221. CodeTypeReferenceCollection yTypeArgs = y.TypeArguments;
  222. if (yTypeArgs.Count == xTypeArgs.Count)
  223. {
  224. foreach (CodeTypeReference xTypeArg in xTypeArgs)
  225. {
  226. foreach (CodeTypeReference yTypeArg in yTypeArgs)
  227. {
  228. if (!this.Equals(xTypeArg, xTypeArg))
  229. return false;
  230. }
  231. }
  232. }
  233. return true;
  234. }
  235. public int GetHashCode(CodeTypeReference obj)
  236. {
  237. return obj.GetHashCode();
  238. }
  239. }
  240. }
  241. }