New Snippet New Snippet Recent Snippets Recent Snippets My Snippets My Snippets Web Code Search Snippets Search
Sign inor Register
Language: C#

WCF Data Service / OData IDispatchBehavior to decode a string-encoded XML fragment and insert it back into the output message.

190 Views
Copy Code Show/Hide Line Numbers
   1:  namespace PersistConfigSettings
   2:  {
   3:      using System;
   4:      using System.Collections.ObjectModel;
   5:      using System.Linq;
   6:      using System.ServiceModel;
   7:      using System.ServiceModel.Channels;
   8:      using System.ServiceModel.Description;
   9:      using System.ServiceModel.Dispatcher;
  10:      using System.Text;
  11:      using System.Xml;
  12:      using System.Xml.Linq;
  13:      using System.Xml.XPath;
  14:   
  15:      [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
  16:      public sealed class ContentToXmlBehaviorAttribute : Attribute, IDispatchMessageInspector, IServiceBehavior
  17:      {
  18:          private const string BareUriKeyword = "$bare";
  19:          private const string UriTemplateMatchResultsKey = "UriTemplateMatchResults";
  20:          private const string DataServicesUri = "http://schemas.microsoft.com/ado/2007/08/dataservices";
  21:          private const string DataServicesUriPrefix = "d";
  22:          private const string ContentXPathExpression = "//d:Content/*[1]";
  23:   
  24:          /// <summary>
  25:          /// Provides the ability to pass custom data to binding elements to support the contract implementation.
  26:          /// </summary>
  27:          /// <param name="serviceDescription">The service description of the service.</param><param name="serviceHostBase">The host of the service.</param><param name="endpoints">The service endpoints.</param><param name="bindingParameters">Custom objects to which binding elements have access.</param>
  28:          public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
  29:          {
  30:              // no op.
  31:          }
  32:   
  33:          /// <summary>
  34:          /// Provides the ability to change run-time property values or insert custom extension objects such as error handlers, message or parameter interceptors, security extensions, and other custom extension objects.
  35:          /// </summary>
  36:          /// <param name="serviceDescription">The service description.</param><param name="serviceHostBase">The host that is currently being built.</param>
  37:          public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  38:          {
  39:              foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
  40:              {
  41:                  foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
  42:                  {
  43:                      endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
  44:                  }
  45:              }
  46:          }
  47:   
  48:          /// <summary>
  49:          /// Provides the ability to inspect the service host and the service description to confirm that the service can run successfully.
  50:          /// </summary>
  51:          /// <param name="serviceDescription">The service description.</param><param name="serviceHostBase">The service host that is currently being constructed.</param>
  52:          public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  53:          {
  54:              // no op.
  55:          }
  56:   
  57:          public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
  58:          {
  59:              if (request.Properties.ContainsKey(UriTemplateMatchResultsKey))
  60:              {
  61:                  var match = (UriTemplateMatch)request.Properties[UriTemplateMatchResultsKey];
  62:                  if (match.QueryParameters.AllKeys.Contains(BareUriKeyword))
  63:                  {
  64:                      match.QueryParameters.Remove(BareUriKeyword);
  65:                      return BareUriKeyword;     
  66:                  }
  67:              }
  68:              return null;
  69:          }
  70:   
  71:          public void BeforeSendReply(ref Message reply, object correlationState)
  72:          {
  73:              var httpResponse = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
  74:              if (httpResponse == null) 
  75:                  throw new InvalidOperationException();
  76:   
  77:              if (httpResponse.StatusCode != System.Net.HttpStatusCode.OK)
  78:                  return;
  79:   
  80:              var reader = reply.GetReaderAtBodyContents();
  81:              reader.ReadStartElement();
  82:              var content = Encoding.UTF8.GetString(reader.ReadContentAsBase64());
  83:   
  84:              var oldReply = reply;
  85:              var xmlString = content.DecodeToXml();
  86:              var bare = correlationState as string;
  87:              if (bare != null && bare.Equals(BareUriKeyword, StringComparison.InvariantCultureIgnoreCase))
  88:              {
  89:                  var nsMgr = new XmlNamespaceManager(new NameTable());
  90:                  nsMgr.AddNamespace(DataServicesUriPrefix, DataServicesUri);
  91:                  var bareContent = XElement.Parse(xmlString).XPathSelectElement(ContentXPathExpression, nsMgr);
  92:                  reply = Message.CreateMessage(MessageVersion.None, string.Empty, new Writer(bareContent.ToString()));
  93:              }
  94:              else
  95:              {
  96:                  reply = Message.CreateMessage(MessageVersion.None, string.Empty, new Writer(xmlString));
  97:              }
  98:              reply.Properties.CopyProperties(oldReply.Properties);
  99:          }
 100:   
 101:          class Writer : BodyWriter
 102:          {
 103:              const string BinaryElementName = "Binary";
 104:              private readonly string _content;
 105:   
 106:              public Writer(string content)
 107:                  : base(false)
 108:              {
 109:                  _content = content;
 110:              }
 111:   
 112:              protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
 113:              {
 114:                  writer.WriteStartElement(BinaryElementName);
 115:                  var buffer = Encoding.UTF8.GetBytes(_content);
 116:                  writer.WriteBase64(buffer, 0, buffer.Length);
 117:                  writer.WriteEndElement();
 118:              }
 119:          }
 120:      }
 121:   
 122:      static class StringExtensions
 123:      { 
 124:          /// <summary>
 125:          /// Poor man&quot;s XML decoder.
 126:          /// </summary>
 127:          /// <param name="string">String to decode to XML.</param>
 128:          /// <returns>XML (as string).</returns>
 129:          public static string DecodeToXml(this string @string)
 130:          {
 131:              return @string.Replace("&lt;", "<")
 132:                            .Replace("&gt;", ">")
 133:                            .Replace("&quot;", "\"");
 134:          }
 135:      }
 136:  }
by Lars Wilhelmsen
  April 04, 2010 @ 1:01pm
Tags:

Add a comment


Report Abuse
brought to you by:
West Wind Techologies


If you find this site useful and use it frequently please consider making a donation to support this free service.
Donate