File: System\Data\Services\DelegateBodyWriter.cs
Project: ndp\fx\src\DataWeb\Server\System.Data.Services.csproj (System.Data.Services)
//---------------------------------------------------------------------
// <copyright file="DelegateBodyWriter.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
//      Provides a BodyWriter that invokes a callback on writing.
// </summary>
//
// @owner  Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.Services
{
    #region Namespaces.
 
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.ServiceModel.Channels;
    using System.Xml;
 
    #endregion Namespaces.
 
    /// <summary>Use this class to handle writing body contents using a callback.</summary>
    internal class DelegateBodyWriter : BodyWriter
    {
        #region Fields.
 
        /// <summary>Service to dispose data source from once the response is written.</summary>
        private readonly IDataService service;
 
        /// <summary>Callback.</summary>
        private readonly Action<Stream> writerAction;
 
        #endregion Fields.
 
        /// <summary>Initializes a new <see cref="DelegateBodyWriter"/> instance.</summary>
        /// <param name="writer">Callback for writing.</param>
        /// <param name="service">Service to dispose data source from once the response is written.</param>
        internal DelegateBodyWriter(Action<Stream> writer, IDataService service)
            : base(false)
        {
            Debug.Assert(service != null, "service != null");
            Debug.Assert(writer != null, "writer != null");
            this.writerAction = writer;
            this.service = service;
        }
 
        /// <summary>Called when the message body is written to an XML file.</summary>
        /// <param name="writer">
        /// An <see cref="XmlDictionaryWriter"/> that is used to write this 
        /// message body to an XML file.
        /// </param>
        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
        {
            Debug.Assert(writer != null, "writer != null");
 
            try
            {
                writer.WriteStartElement(XmlConstants.WcfBinaryElementName);
                using (XmlWriterStream stream = new XmlWriterStream(writer))
                {
                    this.writerAction(stream);
                }
 
                writer.WriteEndElement();
            }
            finally
            {
                if (this.service != null)
                {
                    this.service.DisposeDataSource();
                    HttpContextServiceHost host = this.service.OperationContext.Host.HttpContextServiceHost;
                    if (host != null)
                    {
                        if (host.ErrorFound)
                        {
                            var ctx = System.ServiceModel.OperationContext.Current;
                            if (ctx != null)
                            {
                                ctx.Channel.Abort();
                            }
                        }
                    }
                }
            }
        }
 
        #region Inner types.
 
        /// <summary>Use this class to write to an <see cref="XmlDictionaryWriter"/>.</summary>
        internal class XmlWriterStream : Stream
        {
            /// <summary>Target writer.</summary>
            private XmlDictionaryWriter innerWriter;
 
            /// <summary>Initializes a new <see cref="XmlWriterStream"/> instance.</summary>
            /// <param name="xmlWriter">Target writer.</param>
            internal XmlWriterStream(XmlDictionaryWriter xmlWriter)
            {
                Debug.Assert(xmlWriter != null, "xmlWriter != null");
                this.innerWriter = xmlWriter;
            }
 
            /// <summary>Gets a value indicating whether the current stream supports reading.</summary>
            public override bool CanRead
            {
                get { return false; }
            }
 
            /// <summary>Gets a value indicating whether the current stream supports seeking.</summary>
            public override bool CanSeek
            {
                get { return false; }
            }
 
            /// <summary>Gets a value indicating whether the current stream supports writing.</summary>
            public override bool CanWrite
            {
                get { return true; }
            }
 
            /// <summary>Gets the length in bytes of the stream.</summary>
            public override long Length
            {
                get { throw Error.NotSupported(); }
            }
 
            /// <summary>Gets or sets the position within the current stream.</summary>
            public override long Position
            {
                get { throw Error.NotSupported(); }
                set { throw Error.NotSupported(); }
            }
 
            /// <summary>
            /// Clears all buffers for this stream and causes any buffered 
            /// data to be written to the underlying device.
            /// </summary>
            public override void Flush()
            {
                this.innerWriter.Flush();
            }
 
            /// <summary>
            /// Reads a sequence of bytes from the current stream and 
            /// advances the position within the stream by the number of bytes read.
            /// </summary>
            /// <param name="buffer">
            /// An array of bytes. When this method returns, the buffer contains 
            /// the specified byte array with the values between <paramref name="offset"/> 
            /// and (<paramref name="offset"/> + <paramref name="count"/> - 1) replaced 
            /// by the bytes read from the current source.
            /// </param>
            /// <param name="offset">
            /// The zero-based byte offset in <paramref name="buffer"/> at which to 
            /// begin storing the data read from the current stream.
            /// </param>
            /// <param name="count">
            /// The maximum number of bytes to be read from the current stream.
            /// </param>
            /// <returns>The total number of bytes read into the buffer.</returns>
            public override int Read(byte[] buffer, int offset, int count)
            {
                throw Error.NotSupported();
            }
 
            /// <summary>Sets the position within the current stream.</summary>
            /// <param name="offset">
            /// A byte offset relative to the <paramref name="origin"/> parameter.
            /// </param>
            /// <param name="origin">
            /// A value of type <see cref="SeekOrigin"/> indicating the reference 
            /// point used to obtain the new position.
            /// </param>
            /// <returns>The new position within the current stream.</returns>
            public override long Seek(long offset, SeekOrigin origin)
            {
                throw Error.NotSupported();
            }
 
            /// <summary>Sets the length of the current stream.</summary>
            /// <param name="value">New value for length.</param>
            public override void SetLength(long value)
            {
                throw Error.NotSupported();
            }
 
            /// <summary>
            /// Writes a sequence of bytes to the current stream and advances 
            /// the current position within this stream by the number of 
            /// bytes written. 
            /// </summary>
            /// <param name="buffer">
            /// An array of bytes. This method copies <paramref name="count"/> 
            /// bytes from <paramref name="buffer"/> to the current stream.
            /// </param>
            /// <param name="offset">
            /// The zero-based byte offset in buffer at which to begin copying 
            /// bytes to the current stream.
            /// </param>
            /// <param name="count">
            /// The number of bytes to be written to the current stream.
            /// </param>
            public override void Write(byte[] buffer, int offset, int count)
            {
                this.innerWriter.WriteBase64(buffer, offset, count);
            }
        }
 
        #endregion Inner types.
    }
}