XmlMessageFormatter.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. //------------------------------------------------------------------------------
  2. // <copyright file="XmlMessageFormatter.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. //------------------------------------------------------------------------------
  6. using System;
  7. using System.Collections;
  8. using System.ComponentModel;
  9. using System.Diagnostics.CodeAnalysis;
  10. using System.IO;
  11. using System.Xml;
  12. using System.Xml.Serialization;
  13. namespace Experimental.System.Messaging
  14. {
  15. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter"]/*' />
  16. /// <devdoc>
  17. /// Formatter class that serializes and deserializes objects into
  18. /// and from MessageQueue messages using Xml.
  19. /// </devdoc>
  20. public class XmlMessageFormatter : IMessageFormatter
  21. {
  22. private Type[] targetTypes;
  23. private string[] targetTypeNames;
  24. Hashtable targetSerializerTable = new Hashtable();
  25. private bool typeNamesAdded;
  26. private bool typesAdded;
  27. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter.XmlMessageFormatter"]/*' />
  28. /// <devdoc>
  29. /// Creates a new Xml message formatter object.
  30. /// </devdoc>
  31. public XmlMessageFormatter()
  32. {
  33. this.TargetTypes = new Type[0];
  34. this.TargetTypeNames = new string[0];
  35. }
  36. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter.XmlMessageFormatter1"]/*' />
  37. /// <devdoc>
  38. /// Creates a new Xml message formatter object,
  39. /// using the given properties.
  40. /// </devdoc>
  41. public XmlMessageFormatter(string[] targetTypeNames)
  42. {
  43. this.TargetTypeNames = targetTypeNames;
  44. this.TargetTypes = new Type[0];
  45. }
  46. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter.XmlMessageFormatter2"]/*' />
  47. /// <devdoc>
  48. /// Creates a new Xml message formatter object,
  49. /// using the given properties.
  50. /// </devdoc>
  51. public XmlMessageFormatter(Type[] targetTypes)
  52. {
  53. this.TargetTypes = targetTypes;
  54. this.TargetTypeNames = new string[0];
  55. }
  56. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter.TargetTypeNames"]/*' />
  57. /// <devdoc>
  58. /// Specifies the set of possible types that will
  59. /// be deserialized by the formatter from the
  60. /// message provided.
  61. /// </devdoc>
  62. [MessagingDescription(Res.XmlMsgTargetTypeNames)]
  63. [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
  64. [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  65. public string[] TargetTypeNames
  66. {
  67. get
  68. {
  69. return this.targetTypeNames;
  70. }
  71. set
  72. {
  73. if (value == null)
  74. throw new ArgumentNullException("value");
  75. this.typeNamesAdded = false;
  76. this.targetTypeNames = value;
  77. }
  78. }
  79. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter.TargetTypes"]/*' />
  80. /// <devdoc>
  81. /// Specifies the set of possible types that will
  82. /// be deserialized by the formatter from the
  83. /// message provided.
  84. /// </devdoc>
  85. [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), MessagingDescription(Res.XmlMsgTargetTypes)]
  86. [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
  87. [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
  88. public Type[] TargetTypes
  89. {
  90. get
  91. {
  92. return this.targetTypes;
  93. }
  94. set
  95. {
  96. if (value == null)
  97. throw new ArgumentNullException("value");
  98. this.typesAdded = false;
  99. this.targetTypes = value;
  100. }
  101. }
  102. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter.CanRead"]/*' />
  103. /// <devdoc>
  104. /// When this method is called, the formatter will attempt to determine
  105. /// if the contents of the message are something the formatter can deal with.
  106. /// </devdoc>
  107. public bool CanRead(Message message)
  108. {
  109. if (message == null)
  110. throw new ArgumentNullException("message");
  111. this.CreateTargetSerializerTable();
  112. Stream stream = message.BodyStream;
  113. XmlTextReader reader = new XmlTextReader(stream);
  114. reader.WhitespaceHandling = WhitespaceHandling.Significant;
  115. reader.DtdProcessing = DtdProcessing.Prohibit;
  116. bool result = false;
  117. foreach (XmlSerializer serializer in targetSerializerTable.Values)
  118. {
  119. if (serializer.CanDeserialize(reader))
  120. {
  121. result = true;
  122. break;
  123. }
  124. }
  125. message.BodyStream.Position = 0; // reset stream in case CanRead is followed by Deserialize
  126. return result;
  127. }
  128. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter.Clone"]/*' />
  129. /// <devdoc>
  130. /// This method is needed to improve scalability on Receive and ReceiveAsync scenarios. Not requiring
  131. /// thread safety on read and write.
  132. /// </devdoc>
  133. public object Clone()
  134. {
  135. XmlMessageFormatter formatter = new XmlMessageFormatter();
  136. formatter.targetTypes = targetTypes;
  137. formatter.targetTypeNames = targetTypeNames;
  138. formatter.typesAdded = typesAdded;
  139. formatter.typeNamesAdded = typeNamesAdded;
  140. foreach (Type targetType in targetSerializerTable.Keys)
  141. formatter.targetSerializerTable[targetType] = new XmlSerializer(targetType);
  142. return formatter;
  143. }
  144. /// <internalonly/>
  145. private void CreateTargetSerializerTable()
  146. {
  147. if (!this.typeNamesAdded)
  148. {
  149. for (int index = 0; index < this.targetTypeNames.Length; ++index)
  150. {
  151. Type targetType = Type.GetType(this.targetTypeNames[index], true);
  152. if (targetType != null)
  153. this.targetSerializerTable[targetType] = new XmlSerializer(targetType);
  154. }
  155. this.typeNamesAdded = true;
  156. }
  157. if (!this.typesAdded)
  158. {
  159. for (int index = 0; index < this.targetTypes.Length; ++index)
  160. this.targetSerializerTable[this.targetTypes[index]] = new XmlSerializer(this.targetTypes[index]);
  161. this.typesAdded = true;
  162. }
  163. if (this.targetSerializerTable.Count == 0)
  164. throw new InvalidOperationException(Res.GetString(Res.TypeListMissing));
  165. }
  166. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter.Read"]/*' />
  167. /// <devdoc>
  168. /// This method is used to read the contents from the given message
  169. /// and create an object.
  170. /// </devdoc>
  171. public object Read(Message message)
  172. {
  173. if (message == null)
  174. throw new ArgumentNullException("message");
  175. this.CreateTargetSerializerTable();
  176. Stream stream = message.BodyStream;
  177. XmlTextReader reader = new XmlTextReader(stream);
  178. reader.WhitespaceHandling = WhitespaceHandling.Significant;
  179. reader.DtdProcessing = DtdProcessing.Prohibit;
  180. foreach (XmlSerializer serializer in targetSerializerTable.Values)
  181. {
  182. if (serializer.CanDeserialize(reader))
  183. return serializer.Deserialize(reader);
  184. }
  185. throw new InvalidOperationException(Res.GetString(Res.InvalidTypeDeserialization));
  186. }
  187. /// <include file='doc\XmlMessageFormatter.uex' path='docs/doc[@for="XmlMessageFormatter.Write"]/*' />
  188. /// <devdoc>
  189. /// This method is used to write the given object into the given message.
  190. /// If the formatter cannot understand the given object, an exception is thrown.
  191. /// </devdoc>
  192. public void Write(Message message, object obj)
  193. {
  194. if (message == null)
  195. throw new ArgumentNullException("message");
  196. if (obj == null)
  197. throw new ArgumentNullException("obj");
  198. Stream stream = new MemoryStream();
  199. Type serializedType = obj.GetType();
  200. XmlSerializer serializer = null;
  201. if (this.targetSerializerTable.ContainsKey(serializedType))
  202. serializer = (XmlSerializer)this.targetSerializerTable[serializedType];
  203. else
  204. {
  205. serializer = new XmlSerializer(serializedType);
  206. this.targetSerializerTable[serializedType] = serializer;
  207. }
  208. serializer.Serialize(stream, obj);
  209. message.BodyStream = stream;
  210. //Need to reset the body type, in case the same message
  211. //is reused by some other formatter.
  212. message.BodyType = 0;
  213. }
  214. }
  215. }