Click here to Skip to main content
Click here to Skip to main content

Simplest code to convert an ADO.NET DataTable to an ADODB.Recordset

By , 27 May 2005
 

Introduction

When working with a legacy ASP application, you might want to convert the core business objects to .NET class libraries while still allow the use of existing ASP pages. If those pages are based on manipulating ADODB Recordsets, it can be a real pain to expose them as an ADO.NET DataTable.

The existing examples I've seen are oriented around generating a full-fidelity replica of the DataTable which allows updates. In many situations, this is not needed and the added burden and fragility of these solutions is not warranted.

This article is a quick code snippet that shows what I believe is the simplest possible way to handle the conversion from an ADO.NET DataTable to an ADODB.Recordset, which can then be handled as if it was still coming from the older middleware component.

The Code

There are two functions, designed to be include as static members of some helper class. The main function is simple:

static public ADODB.Recordset ConvertToRecordset(DataTable inTable)
{
    ADODB.Recordset result = new ADODB.Recordset();
    result.CursorLocation = ADODB.CursorLocationEnum.adUseClient;

    ADODB.Fields resultFields = result.Fields;
    System.Data.DataColumnCollection inColumns = inTable.Columns;

    foreach (DataColumn inColumn in inColumns)
    {
        resultFields.Append(inColumn.ColumnName
            , TranslateType(inColumn.DataType)
            , inColumn.MaxLength
            , inColumn.AllowDBNull ? ADODB.FieldAttributeEnum.adFldIsNullable : 
                                     ADODB.FieldAttributeEnum.adFldUnspecified
            , null);
    }

    result.Open(System.Reflection.Missing.Value
            , System.Reflection.Missing.Value
            , ADODB.CursorTypeEnum.adOpenStatic
            , ADODB.LockTypeEnum.adLockOptimistic, 0);

    foreach (DataRow dr in inTable.Rows)
    {
        result.AddNew(System.Reflection.Missing.Value, 
                      System.Reflection.Missing.Value);

        for (int columnIndex = 0; columnIndex < inColumns.Count; columnIndex++)
        {
            resultFields[columnIndex].Value = dr[columnIndex];
        }
    }

    return result;
}

A helper function (which can easily be extended) maps the .NET data types to the correct ADODB field type enumeration.

static ADODB.DataTypeEnum TranslateType(Type columnType)
{
    switch (columnType.UnderlyingSystemType.ToString())
    {
        case "System.Boolean":
            return ADODB.DataTypeEnum.adBoolean;

        case "System.Byte":
            return ADODB.DataTypeEnum.adUnsignedTinyInt;

        case "System.Char":
            return ADODB.DataTypeEnum.adChar;

        case "System.DateTime":
            return ADODB.DataTypeEnum.adDate;

        case "System.Decimal":
            return ADODB.DataTypeEnum.adCurrency;

        case "System.Double":
            return ADODB.DataTypeEnum.adDouble;

        case "System.Int16":
            return ADODB.DataTypeEnum.adSmallInt;

        case "System.Int32":
            return ADODB.DataTypeEnum.adInteger;

        case "System.Int64":
            return ADODB.DataTypeEnum.adBigInt;

        case "System.SByte":
            return ADODB.DataTypeEnum.adTinyInt;

        case "System.Single":
            return ADODB.DataTypeEnum.adSingle;

        case "System.UInt16":
            return ADODB.DataTypeEnum.adUnsignedSmallInt;

        case "System.UInt32":
            return ADODB.DataTypeEnum.adUnsignedInt;

        case "System.UInt64":
            return ADODB.DataTypeEnum.adUnsignedBigInt;

        case "System.String":
        default:
            return ADODB.DataTypeEnum.adVarChar;
    }
}

Gotchas

  • You have to define the fields in the Recordset before you call the Open method or ADODB will give you an error.
  • This is a disconnected Recordset, so it cannot be used to update a table without further handling.
  • It only copies over the "current" values of the columns.
  • It copies all the rows, so be careful with large row-sets.

Other Articles

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

About the Author

Marc Brooks
Web Developer
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionGood Articlememberbala_prodin@india.com25 Apr '13 - 5:01 
Nice posting..
Question'Sort' property doesn't work for the converted Recordsetmemberronnie_bleum14 Oct '10 - 19:46 
Hi,
 
I use your codes and everything looks fine but the 'Sort' property doesn't work for the converted Recordset.
 
My codes are like following:
ADODB.Recordset result = new ADODB.Recordset();
result = ConvertToRecordset(dt);
result.Sort = "field_a";

 
Seems the converted recordset doesn't support 'Sort' property. Any help would be appreciated.
GeneralMethod's type signature is not Interop compatiblemember_Matt_Wilkinson_8 Apr '10 - 6:12 
I am getting this error when I try and convert one of my database table to a recordset. I have narrowed it down to one column which is a System.TimeSpan type. I have tried converting this type to adVariant, adDate, adDBDate, adDBTime, adDBTimeStamp. but all of these produce the same error as above. What type should this be converted to?
GeneralRe: Method's type signature is not Interop compatiblememberMarc Brooks8 Apr '10 - 13:42 
I suspect the best thing would be modelling it as a long, but I don't have any code like that around anymore.
http://musingmarc.blogspot.com

GeneraladVarChar becomes adLongVarCharmemberChad Jones29 Jul '09 - 10:13 
I'm not quite sure why this is happening, so I'm curious if anybody else has seen this problem. I have a DataSet that has some string data types in it. When this code first creates the recordset and inserts the data with the Append method, the data type is adVarChar as it should be given the TranslateType method results (I put a watch on result.Fields["Name"].Type). As soon as the Open method is called on the Recordset, all of those adVarChar types become adLongVarChar.
 
Most things behave fine with this change, except for the fact that I can't set the Sort property on a field with type adLongVarChar.
 
Any ideas as to why this is happening?
Thanks
GeneralRe: adVarChar becomes adLongVarCharmemberMarc Brooks29 Jul '09 - 11:14 
Sounds like the column you are selecting is a text, ntext, varchar(max) or nvarchar(max). Without schema it is very hard to know.Confused | :confused:
 
http://musingmarc.blogspot.com

GeneralRe: adVarChar becomes adLongVarCharmemberChad Jones29 Jul '09 - 11:24 
Thanks for the quick response. Of course, right after I posted I realized that inColumn.MaxLength is always returning -1. That is what is causing the adVarChar to become adLongVarChar when the recordset is opened. When I was populating my DataSet, I only called Fill and missed FillSchema. After I changed my code to the following, things seem to be working great.
 
SqlCommand cmd = new SqlCommand(sql, connectionString);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds);
da.FillSchema(ds, SchemaType.Source);  //missing this line causes problems
 
Thanks for the helpful article!
GeneralThanx! [modified]memberDennis Betten13 Mar '08 - 13:57 
This is just what I was looking for!! Wink | ;)
 
I used your code in my article (http://www.codeproject.com/KB/vb/exportdatabasetoexcel.aspx[^]) which made it much much faster!
 
Gave you credits and a top rating for it...
 
modified on Saturday, March 15, 2008 6:40 AM

GeneralError in parsing a TIME columnmemberfiwi463pfa1 Apr '07 - 23:08 
The provided code is helping on parsing a datatable to a recordset, which we use in a Microsoft Office Spreadsheet Web Component (OWC11).
Unfortunately I get a bunch of errors when a TIME column is involved, I see the time data as for instance "09:45:13" in the datatable, but I get as many "A first chance exception of type 'System.ArgumentException' occurred in ADODB.dll" errors as there are rows...
and after the parsing I have all my data in the OWC except for the Time column, which is empty...
 
Adding a statement
Case "System.TimeSpan"
Return ADODB.DataTypeEnum.adDate
in the TranslateType Function does not resolve anything, even if the type is indeed a System.TimeSpan...
 
Phil
GeneralRe: Error in parsing a TIME columnmemberTim McCurdy19 Sep '07 - 9:37 
It should probably be converted to a "ADODB.DataTypeEnum.adDBTimeStamp" since this is what all OLEDB types need to be for Date / Time values.
Generaladodb to ado.netmembersmurfnz16 Jan '07 - 12:12 
I have worked alot with adodb. I have tryed a couple of times to switch to ado.net, but my project deadlines have never allowed time for the learning curve. Do you know any good articles that would quickly explain how to use ado.net bassed on a general knowledge of adodb. Your article gave me several clues as to how they relate, but i still am missing a few pieces of the puzzel. Like how to connect to a database and how to run a stored procedure.
 
Thanks
General"System.byte[]"memberjwcarroll21 Sep '06 - 5:22 
I am in the process of creating a CCW for a .net assembly for my company. I needed a quick way of converting DataTables to ADODB.Recordset objects and I stumbled upon your article. Excellent code by the way!
 
However, during the execution of my code I ran into a bit of a problem with at SQL type of DBTimeStamp. As I'm sure your aware this is a binary type in SQL, and as such, is treated at an array of bytes. Fair enough. Unfortunately when this snippet of code runs:
 
for (int columnIndex = 0; columnIndex < inColumns.Count; columnIndex++)
{
resultFields[columnIndex].Value = dr[columnIndex];
}

 
I get an exception of type "System.Runtime.InteropServices.COMException". It seems to be when the column of type "System.byte[]" is getting copied over the whole thing blows up. Just wondering if you had a fix for this, or any ideas as to what the problem really is.
 
Thanks,
Josh
GeneralFixmemberjwcarroll21 Sep '06 - 5:29 
Ok, so I am a moron...
 
I realized that the conversion list only accounted for the
 
"System.Byte" type
 
and not the
 
"System.Byte[]" type
 
Not sure how I missed that distinction, but everything is Kosher now Laugh | :laugh: I am going to drink another RedBull and try to wake up Sleepy | :zzz:
 
Thanks,
Josh
GeneralOne more thing...memberjwcarroll21 Sep '06 - 5:37 
Just in case anybody was wondering, a full list of type mappings can be found on MSDN here:
 
https://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconMappingNETDataProviderDataTypesToNETFrameworkDataTypes.asp[^]
 
Also, the line I added to the mapping function looks something like this:
 
case "System.Byte[]":
return ADODB.DataTypeEnum.adBinary;

 
Thanks,
Josh
GeneralRe: One more thing...memberTim McCurdy19 Sep '07 - 9:40 
You should have used "ADODB.DataTypeEnum.adVarBinary" since your byte array can be different lengths. If you are ABSOLUTELY sure that all your columns values are exactly the same length, then adBinary is fine. However, this is usually not the case.
 
Just think of how SQL Server handles Binary and VarBinary data types.
GeneralGreat articlememberDummyDUmb8 Sep '06 - 9:11 
Thanks for the contribution. Smile | :)
The article helped me to solve a problem with AddNew which I wasn't able to solve for a couple of hours.
GeneralRe: Great articlememberMarc Brooks8 Sep '06 - 9:30 
Wow, blast from past. Glad it helped.
 
http://musingmarc.blogspot.com

GeneralProblem AddNewmemberrbarzallo17 May '06 - 7:02 
Hi,
 
I am using the function but I have the following problem
 
1) I have 20 records in DataSet.
 
2) the conversion function is executed well, but when I review the RecordSet appears only one record. Where this the problem?
 

Thanks for its commentaries
GeneralRe: Problem AddNewmemberrbarzallo17 May '06 - 7:39 
Sorry,
 
result.MoveFirst();
GeneralRe: Problem AddNewmemberTim McCurdy19 Sep '07 - 9:41 
You need to call the Update method on the Recordset in order for the new Records to appear:
 
if (rs.EditMode != ADODB.EditModeEnum.adEditNone) rs.Update(Missing.Value, Missing.Value);
GeneralRe: Problem AddNewmemberCleber L.14 Dec '09 - 1:10 
Hi all,
 
First of all I would like to give thanks to Marc Brooks by excelent article.
 
I found the same problem than rbarzallo, I tried the Tim McCurdy's solution: (if (rs.EditMode != ADODB.EditModeEnum.adEditNone) rs.Update(Missing.Value, Missing.Value)) but the problem still persist.
 
Somebody can help me?
Tks,
 
Cleber
GeneralFunction to convert ADODB.DataTypeEnum to System.TypememberMichael Freidgeim12 Nov '05 - 16:37 
I have a complimentary function to your TranslateType that converts ADODB.DataTypeEnum to System.Type .
See FromAdoDbType function in my post
Fill Dataset with rows in a specified range from ADODB.Recordset[^]
 
Michael Freidgeim.
Blog: http://geekswithblogs.net/mnf/
GeneralError trying examplesussJonBrune12 Oct '05 - 10:21 
When I try the example I get the error 'The type or namespace ADODB can not be found.' I assume I need a 'using' directive, but I don't know which one.
GeneralRe: Error trying examplememberFranck Quintana8 Nov '05 - 11:49 
Hi Jon,
You must reference the ADODB Com object inside your project.
The system will create automatically a wrapper .NET <--> COM to enable communication between .net and the com object.
Hope this helps.
Rgds,
Franck.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 27 May 2005
Article Copyright 2005 by Marc Brooks
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid