Search code examples
c#restpersistenceadorecordset

Return ADODB.Recordset from ReST Call as ADODB.Recordset


I am attempting to incrementally take a legacy application into current technologies. I have a C++ COM+ library using ADO on a server. A VB6 application is using the Recordset from a COM+ object based on the aforementioned library. I want to remove the COM+ layer, so I have created a ReST web service with the ASP.NET Web API project type. I cannot seem to return a Recordset like I would other data types using the HTTP results built into C# (i.e. Ok and BadRequest). I've been trying to accomplish this through Recordset persistence. I save the Recordset as a stream as follows:

ADODB._Recordset rs;
string rsString;
SomeLibrary sl = new SomeLibrary();
rs = (ADODB._Recordset)sl.SomeMethod();
Stream stream = new Stream();
rs.Save(stream, PersistFormatEnum.adPersistXML);
rs.Close();
rsString = stream.ReadText();
return Ok(rsString);

The C# client tries to take this persisted ADO Recordset and re-create the object like so:

_Recordset ret = null;
string strResponse = string.Empty;
Does things to call out to the ReST web service...
ret = new Recordset();
byte[] rsByteArray = Encoding.ASCII.GetBytes(strResponse);
ret.Open(new MemoryStream(rsByteArray));

On the last line, I get the following error:

Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.

The persisted XML looks like the following:

<xml xmlns:s='uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882' xmlns:dt='uuid:C2F41010-65B3-11d1-A29F-00AA00C14882' xmlns:rs='urn:schemas-microsoft-com:rowset' xmlns:z='#RowsetSchema'>
<s:Schema id='RowsetSchema'>
    <s:ElementType name='row' content='eltOnly' rs:CommandTimeout='600' rs:ReshapeName='DSRowset1'>
        <s:AttributeType name='ID' rs:number='1'>
            <s:datatype dt:type='int' dt:maxLength='4' rs:precision='10' rs:fixedlength='true' rs:maybenull='false'/>
        </s:AttributeType>
        <s:AttributeType name='c1' rs:name='Some Date' rs:number='2' rs:nullable='true'>
            <s:datatype dt:type='dateTime' rs:dbtype='timestamp' dt:maxLength='16' rs:scale='3' rs:precision='23' rs:fixedlength='true'/>
        </s:AttributeType>
        <s:AttributeType name='Status' rs:number='3' rs:nullable='true'>
            <s:datatype dt:type='ui1' dt:maxLength='1' rs:precision='3' rs:fixedlength='true'/>
        </s:AttributeType>
        <s:AttributeType name='c3' rs:name='File Name' rs:number='4' rs:nullable='true'>
            <s:datatype dt:type='string' rs:dbtype='str' dt:maxLength='2000'/>
        </s:AttributeType>
        <s:AttributeType name='c4' rs:name='User ID' rs:number='5'>
            <s:datatype dt:type='string' rs:dbtype='str' dt:maxLength='32' rs:maybenull='false'/>
        </s:AttributeType>
        <s:extends type='rs:rowbase'/>
    </s:ElementType>
</s:Schema>
<rs:data>
<z:row ID='3319' c1='2017-06-26T08:14:46' Status='2'
     c3='somefile.XML' c4='domain\\user'/>
</rs:data>
</xml>

The objective is to have an ADO Recordset to send back to the VB6 application so that it never knows that the implementation changed other than maybe a new reference to the new C# library instead of the COM+ library.

Is there a bug in what I've written or is this just not possible? If not possible, does anyone know a different path I can take to try to accomplish this task?

EDIT:
The answer on this question was definitely helpful. When I finally got it working, I changed the server to the following:

ADODB.Stream stream = new ADODB.Stream();
rs.Save(stream, PersistFormatEnum.adPersistXML);
rs.Close();
rsString = stream.ReadText();
return Ok(rsString);

And the client to the following:

_Recordset ret = null;
string strResponse = string.Empty;
Does things to call out to the ReST web service...
if (!string.IsNullOrEmpty(strResponse))
{
    strResponse = strResponse.Replace("\\t", "    ").Replace("\\r\\n", " ").Replace("\"", "");
    ret = new Recordset();
    ADODB.Stream stream = new ADODB.Stream();
    stream.Open();
    stream.WriteText(strResponse);
    stream.Position = 0;
    ret.Open(stream);
}

return ret;

The VB6 module can now accept data as if it received it from the COM+ object. I hope this helps someone else that needs to update their technology in cheap increments.


Solution

  • You need to pass ADODB.Stream object in recordset open method like:

    ADODB.Stream strm = new ADODB.Stream();
    strm.Open();
    strm.LoadFromFile(@"D:\XMLRecordset.xml"); 
    Recordset rs = new Recordset();
    rs.Open(strm);
    

    But above (using Stream object) is not working as well - dont know why. But you can use file path to open recordset and will work like:

    Recordset rs = new Recordset();
    rs.Open(@"D:\XMLRecordset.xml");
    

    Also if you don't want to deal with recordset, you can pass List of objects from web api and in VB6 application parse the JSON string to get results (check answer from Ben here Is There a JSON Parser for VB6 / VBA?)