XmlSerializerOperationGenerator.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. using System.Collections.Generic;
  5. using System.ServiceModel.Channels;
  6. using System.ServiceModel;
  7. using System.ServiceModel.Dispatcher;
  8. using System.CodeDom;
  9. using System.Globalization;
  10. using System.Text;
  11. using System.Xml.Serialization;
  12. using System.CodeDom.Compiler;
  13. using System.Runtime.Serialization;
  14. namespace System.ServiceModel.Description
  15. {
  16. class XmlSerializerOperationGenerator : IOperationBehavior, IOperationContractGenerationExtension
  17. {
  18. OperationGenerator operationGenerator;
  19. Dictionary<MessagePartDescription, PartInfo> partInfoTable;
  20. Dictionary<OperationDescription, XmlSerializerFormatAttribute> operationAttributes = new Dictionary<OperationDescription, XmlSerializerFormatAttribute>();
  21. XmlCodeExporter xmlExporter;
  22. SoapCodeExporter soapExporter;
  23. XmlSerializerImportOptions options;
  24. CodeNamespace codeNamespace;
  25. internal XmlSerializerOperationGenerator(XmlSerializerImportOptions options)
  26. {
  27. operationGenerator = new OperationGenerator();
  28. this.options = options;
  29. this.codeNamespace = GetTargetCodeNamespace(options);
  30. partInfoTable = new Dictionary<MessagePartDescription, PartInfo>();
  31. }
  32. static CodeNamespace GetTargetCodeNamespace(XmlSerializerImportOptions options)
  33. {
  34. CodeNamespace targetCodeNamespace = null;
  35. string clrNamespace = options.ClrNamespace ?? string.Empty;
  36. foreach (CodeNamespace ns in options.CodeCompileUnit.Namespaces)
  37. {
  38. if (ns.Name == clrNamespace)
  39. {
  40. targetCodeNamespace = ns;
  41. }
  42. }
  43. if (targetCodeNamespace == null)
  44. {
  45. targetCodeNamespace = new CodeNamespace(clrNamespace);
  46. options.CodeCompileUnit.Namespaces.Add(targetCodeNamespace);
  47. }
  48. return targetCodeNamespace;
  49. }
  50. internal void Add(MessagePartDescription part, XmlMemberMapping memberMapping, XmlMembersMapping membersMapping, bool isEncoded)
  51. {
  52. PartInfo partInfo = new PartInfo();
  53. partInfo.MemberMapping = memberMapping;
  54. partInfo.MembersMapping = membersMapping;
  55. partInfo.IsEncoded = isEncoded;
  56. partInfoTable[part] = partInfo;
  57. }
  58. public XmlCodeExporter XmlExporter
  59. {
  60. get
  61. {
  62. if (this.xmlExporter == null)
  63. {
  64. this.xmlExporter = new XmlCodeExporter(this.codeNamespace, this.options.CodeCompileUnit, this.options.CodeProvider,
  65. this.options.WebReferenceOptions.CodeGenerationOptions, null);
  66. }
  67. return xmlExporter;
  68. }
  69. }
  70. public SoapCodeExporter SoapExporter
  71. {
  72. get
  73. {
  74. if (this.soapExporter == null)
  75. {
  76. this.soapExporter = new SoapCodeExporter(this.codeNamespace, this.options.CodeCompileUnit, this.options.CodeProvider,
  77. this.options.WebReferenceOptions.CodeGenerationOptions, null);
  78. }
  79. return soapExporter;
  80. }
  81. }
  82. OperationGenerator OperationGenerator
  83. {
  84. get { return this.operationGenerator; }
  85. }
  86. internal Dictionary<OperationDescription, XmlSerializerFormatAttribute> OperationAttributes
  87. {
  88. get { return operationAttributes; }
  89. }
  90. void IOperationBehavior.Validate(OperationDescription description)
  91. {
  92. }
  93. void IOperationBehavior.AddBindingParameters(OperationDescription description, BindingParameterCollection parameters)
  94. {
  95. }
  96. void IOperationBehavior.ApplyDispatchBehavior(OperationDescription description, DispatchOperation dispatch) { }
  97. void IOperationBehavior.ApplyClientBehavior(OperationDescription description, ClientOperation proxy) { }
  98. static object contractMarker = new object();
  99. // Assumption: gets called exactly once per operation
  100. void IOperationContractGenerationExtension.GenerateOperation(OperationContractGenerationContext context)
  101. {
  102. if (context == null)
  103. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
  104. if (partInfoTable != null && partInfoTable.Count > 0)
  105. {
  106. Dictionary<XmlMembersMapping, XmlMembersMapping> alreadyExported = new Dictionary<XmlMembersMapping, XmlMembersMapping>();
  107. foreach (MessageDescription message in context.Operation.Messages)
  108. {
  109. foreach (MessageHeaderDescription header in message.Headers)
  110. GeneratePartType(alreadyExported, header, header.Namespace);
  111. MessageBodyDescription body = message.Body;
  112. bool isWrapped = (body.WrapperName != null);
  113. if (OperationFormatter.IsValidReturnValue(body.ReturnValue))
  114. GeneratePartType(alreadyExported, body.ReturnValue, isWrapped ? body.WrapperNamespace : body.ReturnValue.Namespace);
  115. foreach (MessagePartDescription part in body.Parts)
  116. GeneratePartType(alreadyExported, part, isWrapped ? body.WrapperNamespace : part.Namespace);
  117. }
  118. }
  119. XmlSerializerOperationBehavior xmlSerializerOperationBehavior = context.Operation.Behaviors.Find<XmlSerializerOperationBehavior>() as XmlSerializerOperationBehavior;
  120. if (xmlSerializerOperationBehavior == null)
  121. return;
  122. XmlSerializerFormatAttribute xmlSerializerFormatAttribute = (xmlSerializerOperationBehavior == null) ? new XmlSerializerFormatAttribute() : xmlSerializerOperationBehavior.XmlSerializerFormatAttribute;
  123. OperationFormatStyle style = xmlSerializerFormatAttribute.Style;
  124. operationGenerator.GenerateOperation(context, ref style, xmlSerializerFormatAttribute.IsEncoded, new WrappedBodyTypeGenerator(context), new Dictionary<MessagePartDescription, ICollection<CodeTypeReference>>());
  125. context.ServiceContractGenerator.AddReferencedAssembly(typeof(System.Xml.Serialization.XmlTypeAttribute).Assembly);
  126. xmlSerializerFormatAttribute.Style = style;
  127. context.SyncMethod.CustomAttributes.Add(OperationGenerator.GenerateAttributeDeclaration(context.Contract.ServiceContractGenerator, xmlSerializerFormatAttribute));
  128. AddKnownTypes(context.SyncMethod.CustomAttributes, xmlSerializerFormatAttribute.IsEncoded ? SoapExporter.IncludeMetadata : XmlExporter.IncludeMetadata);
  129. DataContractSerializerOperationGenerator.UpdateTargetCompileUnit(context, this.options.CodeCompileUnit);
  130. }
  131. private void AddKnownTypes(CodeAttributeDeclarationCollection destination, CodeAttributeDeclarationCollection source)
  132. {
  133. foreach (CodeAttributeDeclaration attribute in source)
  134. {
  135. CodeAttributeDeclaration knownType = ToKnownType(attribute);
  136. if (knownType != null)
  137. {
  138. destination.Add(knownType);
  139. }
  140. }
  141. }
  142. // Convert [XmlInclude] or [SoapInclude] attribute to [KnownType] attribute
  143. private CodeAttributeDeclaration ToKnownType(CodeAttributeDeclaration include)
  144. {
  145. if (include.Name == typeof(SoapIncludeAttribute).FullName || include.Name == typeof(XmlIncludeAttribute).FullName)
  146. {
  147. CodeAttributeDeclaration knownType = new CodeAttributeDeclaration(new CodeTypeReference(typeof(ServiceKnownTypeAttribute)));
  148. foreach (CodeAttributeArgument argument in include.Arguments)
  149. {
  150. knownType.Arguments.Add(argument);
  151. }
  152. return knownType;
  153. }
  154. return null;
  155. }
  156. private void GeneratePartType(Dictionary<XmlMembersMapping, XmlMembersMapping> alreadyExported, MessagePartDescription part, string partNamespace)
  157. {
  158. if (!partInfoTable.ContainsKey(part))
  159. return;
  160. PartInfo partInfo = partInfoTable[part];
  161. XmlMembersMapping membersMapping = partInfo.MembersMapping;
  162. XmlMemberMapping memberMapping = partInfo.MemberMapping;
  163. if (!alreadyExported.ContainsKey(membersMapping))
  164. {
  165. if (partInfo.IsEncoded)
  166. SoapExporter.ExportMembersMapping(membersMapping);
  167. else
  168. XmlExporter.ExportMembersMapping(membersMapping);
  169. alreadyExported.Add(membersMapping, membersMapping);
  170. }
  171. CodeAttributeDeclarationCollection additionalAttributes = new CodeAttributeDeclarationCollection();
  172. if (partInfo.IsEncoded)
  173. SoapExporter.AddMappingMetadata(additionalAttributes, memberMapping, false/*forceUseMemberName*/);
  174. else
  175. XmlExporter.AddMappingMetadata(additionalAttributes, memberMapping, partNamespace, false/*forceUseMemberName*/);
  176. part.BaseType = GetTypeName(memberMapping);
  177. operationGenerator.ParameterTypes.Add(part, new CodeTypeReference(part.BaseType));
  178. operationGenerator.ParameterAttributes.Add(part, additionalAttributes);
  179. }
  180. internal string GetTypeName(XmlMemberMapping member)
  181. {
  182. string typeName = member.GenerateTypeName(options.CodeProvider);
  183. // If it is an array type, get the array element type name instead
  184. string comparableTypeName = typeName.Replace("[]", null);
  185. if (codeNamespace != null && !string.IsNullOrEmpty(codeNamespace.Name))
  186. {
  187. foreach (CodeTypeDeclaration typeDecl in codeNamespace.Types)
  188. {
  189. if (typeDecl.Name == comparableTypeName)
  190. {
  191. typeName = codeNamespace.Name + "." + typeName;
  192. }
  193. }
  194. }
  195. return typeName;
  196. }
  197. class PartInfo
  198. {
  199. internal XmlMemberMapping MemberMapping;
  200. internal XmlMembersMapping MembersMapping;
  201. internal bool IsEncoded;
  202. }
  203. internal class WrappedBodyTypeGenerator : IWrappedBodyTypeGenerator
  204. {
  205. OperationContractGenerationContext context;
  206. public WrappedBodyTypeGenerator(OperationContractGenerationContext context)
  207. {
  208. this.context = context;
  209. }
  210. public void ValidateForParameterMode(OperationDescription operation)
  211. {
  212. }
  213. public void AddMemberAttributes(XmlName messageName, MessagePartDescription part, CodeAttributeDeclarationCollection importedAttributes, CodeAttributeDeclarationCollection typeAttributes, CodeAttributeDeclarationCollection fieldAttributes)
  214. {
  215. if (importedAttributes != null)
  216. fieldAttributes.AddRange(importedAttributes);
  217. }
  218. public void AddTypeAttributes(string messageName, string typeNS, CodeAttributeDeclarationCollection typeAttributes, bool isEncoded)
  219. {
  220. // we do not need top-level attibutes for the encoded SOAP
  221. if (isEncoded)
  222. return;
  223. XmlTypeAttribute xmlType = new XmlTypeAttribute();
  224. xmlType.Namespace = typeNS;
  225. typeAttributes.Add(OperationGenerator.GenerateAttributeDeclaration(context.Contract.ServiceContractGenerator, xmlType));
  226. }
  227. }
  228. }
  229. }