EagleMl Web Service - Custom Classes and Overrides

This section describes some additional actions you should fulfill to make your work more effective.

Create a CustomTextMessageBindingElement class.

internal class CustomTextMessageBindingElement : MessageEncodingBindingElement, IWsdlExportExtension
    {
        #region Fields
        private MessageVersion _msgVersion;
        private string _mediaType;
        private string _encoding;
        private readonly XmlDictionaryReaderQuotas _readerQuotas;
        #endregion
        #region Properties
        public override MessageVersion MessageVersion
        {
            get { return _msgVersion; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                _msgVersion = value;
            }
        }
        public string MediaType
        {
            get { return _mediaType; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                _mediaType = value;
            }
        }
        public string Encoding
        {
            get { return _encoding; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("value");
                _encoding = value;
            }
        }
        // This encoder does not enforces any quotas for the unsecure messages. The
        // quotas are enforced for the secure portions of messages when this encoder
        // is used in a binding that is configured with security.
        public XmlDictionaryReaderQuotas ReaderQuotas
        {
            get { return _readerQuotas; }
        }
        #endregion
        #region Constructors
        private CustomTextMessageBindingElement(CustomTextMessageBindingElement binding)
            : this(binding.Encoding, binding.MediaType, binding.MessageVersion)
        {
            _readerQuotas = new XmlDictionaryReaderQuotas();
            binding.ReaderQuotas.CopyTo(_readerQuotas);
        }
        public CustomTextMessageBindingElement(string encoding, string mediaType,
                                                MessageVersion msgVersion)
        {
            if (encoding == null)
                throw new ArgumentNullException("encoding");
            if (mediaType == null)
                throw new ArgumentNullException("mediaType");
            if (msgVersion == null)
                throw new ArgumentNullException("msgVersion");
            _msgVersion = msgVersion;
            _mediaType = mediaType;
            _encoding = encoding;
            _readerQuotas = new XmlDictionaryReaderQuotas();
        }
        public CustomTextMessageBindingElement(string encoding, string mediaType)
            : this(encoding, mediaType, MessageVersion.Soap11WSAddressing10)
        {
                    }
        public CustomTextMessageBindingElement(string encoding)
            : this(encoding, "text/xml")
        {
                    }
        public CustomTextMessageBindingElement()
            : this("UTF-8")
        {
        }
        #endregion
        #region Overrides
        public override BindingElement Clone()
        {
            return new CustomTextMessageBindingElement(this);
        }
        public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            context.BindingParameters.Add(this);
            return context.BuildInnerChannelFactory<TChannel>();
        }
        public override bool CanBuildChannelFactory<TChannel>(BindingContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            return context.CanBuildInnerChannelFactory<TChannel>();
        }
        public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            context.BindingParameters.Add(this);
            return context.BuildInnerChannelListener<TChannel>();
        }
        public override bool CanBuildChannelListener<TChannel>(BindingContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            context.BindingParameters.Add(this);
            return context.CanBuildInnerChannelListener<TChannel>();
        }
        public override T GetProperty<T>(BindingContext context)
        {
            if (typeof(T) == typeof(XmlDictionaryReaderQuotas))
                return (T)(object)_readerQuotas;
            return base.GetProperty<T>(context);
        }
        #endregion
        #region IMessageEncodingBindingElement Members
        public override MessageEncoderFactory CreateMessageEncoderFactory()
        {
            return new CustomTextMessageEncoderFactory(MediaType, Encoding, MessageVersion);
        }
        #endregion
        #region IWsdlExportExtension Members
        void IWsdlExportExtension.ExportContract(WsdlExporter exporter, WsdlContractConversionContext context)
        { }
        void IWsdlExportExtension.ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
        {
            // The MessageEncodingBindingElement is responsible for ensuring that the WSDL has the correct
            // SOAP version. We can delegate to the WCF implementation of TextMessageEncodingBindingElement for this.
            var mebe = new TextMessageEncodingBindingElement { MessageVersion = _msgVersion };
            ((IWsdlExportExtension)mebe).ExportEndpoint(exporter, context);
        }
        #endregion
    }

The most important methods are:

public CustomTextMessageBindingElement(string encoding)
and
public CustomTextMessageBindingElement()

In these methods, set the default contentType and encoding (text/xml and UTF-8).

Create CustomTextMessageEncoderFactory and CustomTextMessageEncoder classes.

public class CustomTextMessageEncoderFactory : MessageEncoderFactory
    {
        private MessageEncoder encoder;
        private MessageVersion version;
        private string mediaType;
        private string charSet;
        internal CustomTextMessageEncoderFactory(string mediaType, string charSet,
            MessageVersion version)
        {
            this.version = version;
            this.mediaType = mediaType;
            this.charSet = charSet;
            this.encoder = new CustomTextMessageEncoder(this);
        }
        public override MessageEncoder Encoder
        {
            get
            {
                return this.encoder;
            }
        }
        public override MessageVersion MessageVersion
        {
            get
            {
                return this.version;
            }
        }
        internal string MediaType
        {
            get
            {
                return this.mediaType;
            }
        }
        internal string CharSet
        {
            get
            {
                return this.charSet;
            }
        }
    }

CustomTextMessageEncoderFactory is a support class. The CustomTextMessageEncoder class is as follows:

public class CustomTextMessageEncoder : MessageEncoder
    {      
        private CustomTextMessageEncoderFactory factory;
        private XmlWriterSettings writerSettings;
        private string contentType;
        public CustomTextMessageEncoder(CustomTextMessageEncoderFactory factory)
        {
            this.factory = factory;
            this.writerSettings = new XmlWriterSettings();
            this.writerSettings.Encoding = Encoding.GetEncoding(factory.CharSet);
            this.contentType = string.Format("{0}; charset={1}",
                this.factory.MediaType, this.writerSettings.Encoding.HeaderName);
        }
        public override string ContentType
        {
            get
            {
                return this.contentType;
            }
        }
        public override string MediaType
        {
            get
            {
                return factory.MediaType;
            }
        }
        public override MessageVersion MessageVersion
        {
            get
            {
                return this.factory.MessageVersion;
            }
        }
        public override System.ServiceModel.Channels.Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
        {
            byte[] msgContents = new byte[buffer.Count];
            Array.Copy(buffer.Array, buffer.Offset, msgContents, 0, msgContents.Length);
            bufferManager.ReturnBuffer(buffer.Array);
            MemoryStream stream = new MemoryStream(msgContents);
            return ReadMessage(stream, int.MaxValue);
        }
        public override System.ServiceModel.Channels.Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
        {
            try
            {
                XmlReader reader = XmlReader.Create(stream);
                return System.ServiceModel.Channels.Message.CreateMessage(reader, maxSizeOfHeaders, this.MessageVersion);
            }
            catch (Exception e)
            {
                stream.Position = 0;
                string value="";
                using (var strreader = new StreamReader(stream, Encoding.UTF8))
                {
                    value = strreader.ReadToEnd();
                }
                if (value.Contains("<ERROR>"))
                {
                    int k = value.IndexOf("<ERROR>");
                    value=value.Substring(k + 7);
                    k = value.LastIndexOf("</ERROR>");
                    if (k > 0)
                    {
                        value = value.Substring(0,k);
                    }
                }
               
                System.ServiceModel.FaultCode fc = new System.ServiceModel.FaultCode("Receiver");
                System.ServiceModel.Channels.Message m = Message.CreateMessage(this.MessageVersion, fc, value, "fault");
                return m;
            }
        }
        public override ArraySegment<byte> WriteMessage(System.ServiceModel.Channels.Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
        {
            MemoryStream stream = new MemoryStream();
          
            XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
            message.WriteMessage(writer);
            writer.Close();
            byte[] messageBytes = stream.GetBuffer();
            int messageLength = (int)stream.Position;
            stream.Close();
            int totalLength = messageLength + messageOffset;
            byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
            Array.Copy(messageBytes, 0, totalBytes, messageOffset, messageLength);
            ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
            return byteArray;
        }
      
      
        public override void WriteMessage(System.ServiceModel.Channels.Message message, Stream stream)
        {
            XmlWriter writer = XmlWriter.Create(stream, this.writerSettings);
            message.WriteMessage(writer);
            writer.Close();
        }
        public override bool IsContentTypeSupported(string contentType)
        {
            if (contentType == "text/plain" || contentType== "text/xml")
                return true;
            return base.IsContentTypeSupported(contentType);
        }       
    }

The CustomTextMessageEncoder class requires the following fixes:

  • The content type fix should be made in the IsContentTypeSupported(string contentType) method. Fix possible incorrect contentTypes from EagleMlWebService.
  • Non-soap messages processing fix. The fix should be made in the ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) method.

Add the GenerateUID() method which generates a unique correlationID.

// unique id generation
        private static string GenerateUID()
        {
            long i = 1;
            foreach (byte b in Guid.NewGuid().ToByteArray())
            {
                i *= ((int)b + 1);
            }
            return string.Format("{0:x}", i - DateTime.Now.Ticks).ToUpper();
        }