简体   繁体   中英

ADO.NET DataSet.Load() doesn't seem to handle empty tables correctly

AFAIK you can load one DataSet from another DataSet via IDataReader ie

new DataSet().Load(existingDataSet.CreateDataReader());
// should now have two DataSets with the same contents

I haven't been able to find any documentation that suggests this is not possible. So, can anyone explain, why the following NUnit test fails?

[Test]
public void Should_be_able_to_load_one_dataset_from_another()
{
    var tableA = new DataTable("A");
    //tableA.Columns.Add("ColumnA", typeof(int));

    var tableB = new DataTable("B");
    tableB.Columns.Add("ColumnB", typeof(int));

    var tableC = new DataTable("C");

    using (var original = new DataSet())
    {
        original.Tables.Add(tableA);
        original.Tables.Add(tableB);
        original.Tables.Add(tableC);

        using (var expectedReader = original.CreateDataReader())
        using (var actual = new DataSet())
        {
            actual.Load(expectedReader, LoadOption.OverwriteChanges, "A", "B", "C");

            Assert.That(actual.HasErrors, Is.False);

            for (var i = 0; i < original.Tables.Count; i++)
                Assert.That(actual.Tables[i].Columns.Count, Is.EqualTo(original.Tables[i].Columns.Count), 
                    "Table {0} had the wrong number of columns.", i); // fails here
        }
    }
}

The test passes if you uncomment ColumnA. So it would seem there is possibly a bug in DataSet.Load() that, after encountering a table with no columns, the next table will be imported with no columns either. Note the empty table is not affected, only the table immediately following it, so if it is only the last table that is empty the test will pass.

Have I found a bug in the BCL? Or is there an explanation for this behaviour?

Before I looked into the code, I was skeptical that this could be a bug, but now I believe it is.

Looking at the DataSet.Load method in Reflector, it goes through the table names provided and creates the tables if they do not exist. So in this case, it creates tables "A", "B", and "C".

Then it loads the data, calling through the following methods and passing the target tables in the destination data set as a DataTable[] :

  • DataSet.Load(IDataReader, LoadOption, FillErrorEventHandler, params DataTable[])
  • LoadAdapter.FillFromReader(DataTable[], IDataReader, int, int)
  • LoadAdapter.Fill(DataTable[], IDataReader, int, int)

In this last Fill method, it loops through the DataTable[] to fill each table, but it only fills the table if it has fields:

if (container.FieldCount > 0)
{
    if ((0 < i) && !this.FillNextResult(container))
    {
        goto Label_00DE;
    }
    int num4 = this.FillFromReader(null, dataTables[i], null, container, startRecord, maxRecords, null, null);
    if (i == 0)
    {
        num2 = num4;
    }
}

Note that when it fills the table, it also calls NextResult on the reader (in FillNextResult ), but there is no else to call NextResult if the FieldCount is 0, and that throws everything afterwards out of synch.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM