Streaming Oracle Database LOB Data Types Using the WCF Channel Model

The Microsoft BizTalk Adapter for Oracle Database supports end-to-end streaming of LOB data for certain operations. The sections in this topic describe how to implement streaming for LOB data when you use the WCF channel model.

For background information about how the adapter supports streaming of LOB data types, see Streaming large object data types in Oracle Database adapter. You should read this topic before proceeding.

A sample that demonstrates LOB data streaming is available in the SDK samples included with the Oracle Database adapter. For more information, see Samples in the SDK.

Streaming Outbound Messages to the Adapter

The adapter supports end-to-end LOB data streaming for the request message for the UpdateLOB operation.

To support end-to-end streaming on UpdateLOB operations in the WCF channel model, you must:

  1. Set the UseAmbientTransaction binding property to true.

  2. Implement a System.ServiceModel.Channels.BodyWriter that is capable of streaming the LOB data (performing node-value streaming on the LOB data).

  3. Perform the UpdateLOB operation within a transaction scope.

  4. Create the System.ServiceModel.Message used to invoke the operation by supplying the message body with this BodyWriter using an appropriate overload of the Message.Create method.

Setting the UseAmbientTransaction Binding Property

The following example shows how to create a binding for the Oracle Database adapter and set the UseAmbientTransaction binding property.

// Create binding  
OracleDBBinding odbBinding = new OracleDBBinding();  
  
//set the binding property  
binding.UseAmbientTransaction = true;  
  

Implementing a BodyWriter

The following example shows an implementation of a BodyWriter that performs node-value streaming.

/// <summary>  
/// This class overrides the OnWriteBodyContents function to do node-value streaming  
/// </summary>  
class StreamingBodyWriter : BodyWriter, IDisposable  
{  
    XmlReader m_reader = null;  
  
    int m_chunkSize;  
    /// <summary>  
    /// Initializes the body writer  
    /// </summary>  
    /// <param name="reader">Reader for input</param>  
    /// <param name="chunkSize">The chunksize in which the data is passed to adapter</param>  
    public StreamingBodyWriter(XmlReader reader, int chunkSize)  
        : base(false)  
    {  
        m_reader = reader;  
        if (chunkSize \<= 0)  
            throw new ApplicationException("ChunkSize should be a positive value");  
        m_chunkSize = chunkSize;  
    }  
  
    protected override void OnWriteBodyContents(XmlDictionaryWriter writer)  
    {  
        if (m_reader == null)  
            throw new ApplicationException("Reader cannot be null");  
  
        while (m_reader.Read())  
        {  
            switch (m_reader.NodeType)  
            {  
                case XmlNodeType.Element:  
                    writer.WriteStartElement(m_reader.LocalName, m_reader.NamespaceURI);  
                    break;  
                case XmlNodeType.Text:  
                    #region Streaming Code  
                    char[] tempBuffer = new char[m_chunkSize];  
                    int length = 0;  
                    while ((length = m_reader.ReadValueChunk(tempBuffer, 0, m_chunkSize)) > 0)  
                    {  
                        writer.WriteString(new String(tempBuffer, 0, length));  
                    }  
                    #endregion  
                    break;  
                case XmlNodeType.EndElement:  
                    writer.WriteEndElement();  
                    break;  
            }  
        }  
  
    }  
  
    #region IDisposable Members  
  
    public void Dispose()  
    {  
        if (m_reader != null)  
        {  
            m_reader.Close();  
            m_reader = null;  
        }  
    }  
  
    #endregion  
}  

Perform the Operations Within a Transaction Scope

The following example shows how to perform operations within a transaction scope.

// Create a transaction scope  
using(TransactionScope tx = new TransactionScope())  
{  
  // perform operations within the transaction  
  // ...  
  // ...  
  
  //Complete the transaction  
  tx.Complete()  
}  
  

Creating a Message by using a BodyWriter

The following example shows how to create an UpdateLOB request message using the BodyWriter in the preceding example. The message data is read from a file.

// Create a transaction scope  
using(TransactionScope tx = new TransactionScope())  
{  
    XmlReader readerIn = XmlReader.Create ("updatelob.xml");  
    // StreamingBodyWrtier class is responsible for streaming  
    StreamingBodyWriter stBW = new StreamingBodyWriter(readerIn, chunkSize);  
  
    Message InputMsg = Message.CreateMessage(MessageVersion.Default,  
    "http://Microsoft.LobServices.OracleDB/2007/03/SCOTT/Table/CUSTOMER/UpdateLOB",   
    stBW);  
  
    //Send the request message and get the output message  
    OutputMsg = channel.Request(InputMsg);  
  
    tx.Complete();  
}  
  

Streaming Inbound Messages from the Adapter

The adapter supports end-to-end LOB data streaming for the following inbound messages:

  • Response message for functions with OUT or IN OUT parameters that contain LOB data. Note that RECORD TYPE parameters can contain LOB data columns.

  • Response message for functions with OUT REF CURSOR parameters (or return values) that contain LOB data. This includes the output side of IN OUT REF CURSOR parameters.

  • Response message for procedures with IN or IN OUT parameters that contain LOB data. Note that RECORD TYPE parameters can contain LOB data columns.

  • Response message for procedures with OUT REF CURSOR parameters that contain LOB data. This includes the output side of IN OUT REF CURSOR parameters

  • Response message for SQLEXECUTE operations that return result sets that contain LOB data.

  • Response message for Table or view Select operations that return LOB data in the result set.

  • Request message for the (inbound) POLLINGSTMT operation

    To support end-to-end streaming on an inbound message in the WCF channel model, you must:

  1. Implement a System.Xml.XmlDictionaryWriter that is capable of streaming the LOB data (performing node-value streaming on the LOB data).

  2. Consume the Message by invoking WriteBodyContents method with this XmlDictionaryWriter.

Implementing an XmlDictionaryWriter

The following example shows an implementation of an XmlDictionaryWriter that performs node-value streaming.

using System;  
using System.Xml;  
using System.Text;  
  
class FileXmlWriter : XmlDictionaryWriter  
{  
    XmlTextWriter xts;  
  
    public FileXmlWriter(string file)  
    {  
        xts = new XmlTextWriter(file, Encoding.UTF8);  
    }  
  
    public override void WriteBase64(byte[] buffer, int index, int count)  
    {  
        xts.WriteBase64(buffer, index, count);  
    }  
  
    public override void WriteCData(string text)  
    {  
        xts.WriteCData(text);  
    }  
  
    public override void WriteCharEntity(char ch)  
    {  
        xts.WriteCharEntity(ch);  
    }  
  
    public override void WriteChars(char[] buffer, int index, int count)  
    {  
        xts.WriteChars(buffer, index, count);  
    }  
  
    public override void WriteComment(string text)  
    {  
        xts.WriteComment(text);  
    }  
  
    public override void WriteDocType(string name, string pubid, string sysid, string subset)  
    {  
        xts.WriteDocType(name, pubid, sysid, subset);  
    }  
  
    public override void WriteEndAttribute()  
    {  
        xts.WriteEndAttribute();  
    }  
  
    public override void WriteEndDocument()  
    {  
        xts.WriteEndDocument();  
    }  
  
    public override void WriteEndElement()  
    {  
        xts.WriteEndElement();  
    }  
  
    public override void WriteEntityRef(string name)  
    {  
        xts.WriteEntityRef(name);  
    }  
  
    public override void WriteFullEndElement()  
    {  
        xts.WriteFullEndElement();  
    }  
  
    public override void WriteProcessingInstruction(string name, string text)  
    {  
        xts.WriteProcessingInstruction(name, text);  
    }  
  
    public override void WriteRaw(string data)  
    {  
        xts.WriteRaw(data);  
    }  
  
    public override void WriteRaw(char[] buffer, int index, int count)  
    {  
        xts.WriteRaw(buffer, index, count);  
    }  
  
    public override void WriteStartAttribute(string prefix, string localName, string ns)  
    {  
        xts.WriteStartAttribute(prefix, localName, ns);  
    }  
  
    public override void WriteStartDocument(bool standalone)  
    {  
        xts.WriteStartDocument(standalone);  
    }  
  
    public override void WriteStartDocument()  
    {  
        xts.WriteStartDocument();  
    }  
  
    public override void WriteStartElement(string prefix, string localName, string ns)  
    {  
        xts.WriteStartElement(localName);  
    }  
  
    public override void WriteString(string text)  
    {  
        xts.WriteString(text);  
    }  
  
    public override void WriteSurrogateCharEntity(char lowChar, char highChar)  
    {  
        xts.WriteSurrogateCharEntity(lowChar, highChar);  
    }  
  
    public override void WriteWhitespace(string ws)  
    {  
        xts.WriteWhitespace(ws);  
    }  
  
    public override void Close()  
    {  
        xts.Close();  
    }  
  
    public override void Flush()  
    {  
        xts.Flush();  
    }  
  
    public override string LookupPrefix(string ns)  
    {  
        return xts.LookupPrefix(ns);  
    }  
  
    public override WriteState WriteState  
    {  
        get { return xts.WriteState; }  
    }  
  
}  

Consuming a Message by using an XmlDictionaryWriter

The following example shows how to consume a table Select response message using the FileXmlWriter implemented in the preceding example. (The FileWriter class was created by sub-classing XmlDictionaryWriter.) The example uses an IRequestChannel channel to invoke the Select operation. The details of the channel creation have been omitted. The Select request message is read from a file and the Select response message is written to a file.

// Read Select message body from a file  
XmlReader readerIn = XmlReader.Create("select.xml");  
Message InputMsg = Message.CreateMessage(MessageVersion.Default,  
    "http://Microsoft.LobServices.OracleDB/2007/03/SCOTT/Table/CUSTOMER/Select", readerIn);  
  
Message OutputMsg = channel.Request(InputMsg);  
  
// Streaming response message to select_output.xml using the custom XmlDictionaryWriter;  
FileXmlWriter fileXmlWriter = new FileXmlWriter("select_output.xml");  
OutputMsg.WriteBodyContents(fileXmlWriter);  
fileXmlWriter.Flush();  
fileXmlWriter.Close();  
  
// Streaming complete close output message;  
OutputMsg.Close();  

The following XML shows the request message (contents of the select.xml file) for the Select operation. The CUSTOMER table contains a BLOB column named PHOTO.

<Select xmlns="http://Microsoft.LobServices.OracleDB/2007/03/SCOTT/Table/CUSTOMER">  
  <COLUMN_NAMES>*</COLUMN_NAMES>  
  <FILTER>NAME='Kim Ralls'</FILTER>  
</Select>  

See Also

Develop Oracle Database applications using the WCF Channel Model