tags:

views:

2355

answers:

6

How do I take a DataTable and convert it to a List?

I've included some code below in both C# and VB.NET, the issue with both of these is that we create a new object to return the data, which is very costly. I need to return a reference to the object.

The DataSetNoteProcsTableAdapters.up_GetNoteRow object does implement the INote interface.

I am using ADO.NET, along with .NET 3.5

c# code

public static IList<INote> GetNotes() 
{ 
    DataSetNoteProcsTableAdapters.up_GetNoteTableAdapter adapter =
        new DataSetNoteProcsTableAdapters.up_GetNoteTableAdapter(); 
    DataSetNoteProcs.up_GetNoteDataTable table =
        new DataSetNoteProcs.up_GetNoteDataTable(); 

    IList<INote> notes = new List<INote>(); 

    adapter.Connection = DataAccess.ConnectionSettings.Connection; 
    adapter.Fill(table); 

    foreach (DataSetNoteProcs.up_GetNoteRow t in table) { 
        notes.Add((INote)t); 
    } 

    return notes;
}

VB.NET Code

Public Shared Function GetNotes() As IList(Of INote)
    Dim adapter As New DataSetNoteProcsTableAdapters.up_GetNoteTableAdapter
    Dim table As New DataSetNoteProcs.up_GetNoteDataTable

    Dim notes As IList(Of INote) = New List(Of INote)

    adapter.Connection = DataAccess.ConnectionSettings.Connection
    adapter.Fill(table)

    For Each t As DataSetNoteProcs.up_GetNoteRow In table
        notes.Add(CType(t, INote))
    Next

    Return notes
End Function
A: 

If the properties of the list match the field names in the datatable you should be able to create some kind of generic reflection routine.

Craig
But is needs not to create a new object, it must reference the object.
Coppermill
A: 

Do you want to reference the rows of the table? In that case you should use the DataTable.Rows property.

Try this:

notes.AddRange(table.Rows);

If the table rows implement INote then this should work.

Alternatively you could do as you did above:

foreach (INote note in table.Rows)
{
  notes.Add(note)
}
Rune Grimstad
This is on table.RowsWarning 1 Runtime errors might occur when converting 'System.Data.DataRowCollection' to 'System.Collections.Generic.IList(Of.INote)'This is generating a new object, which is what I am trying to avoid
Coppermill
No, this should not create new objects. All you are doing is casting the object to an other type.
Rune Grimstad
+1  A: 

Why not pass the DataTable into to the function instead of instantiating it? That would simply contain a reference.

That's way too simple an answer too be worthwhile to you I'm sure, but I don't see how it doesn't solve your problem.

cerhart
I am after an INote to be returned
Coppermill
+1  A: 

No, creating a list is not costly. Compared to creating the data table and fetching the data from the database, it's very cheap.

You can make it even cheaper by creating the list after populating the table, so that you can set the initial capacity to the number of rows that you will put in it:

IList<INote> notes = new List<INote>(table.Rows.Count);
Guffa
surely you meanIList<INote> notes = new List<INote>(table.Rows)if you do you get the following error 'Public Sub New(collection AsIEnumerable(Of INote))':implicit conversions from 'DataRowCollection' to 'IEnumerable(Of INote)'.
Coppermill
No he means what he wrote. That will cause the list to be set to the size of the results set.
Cipher
A: 

No sure if this is what your looking for but you could try something like this.

    public class Category
{
    private int _Id;
    public int Id
    {
        get { return _Id; }
        set { _Id = value; }
    }

    private string _Name = null;
    public string Name
    {
        get { return _Name; }
        set { _Name = value; }
    }

    public Category()
    {}

    public static List<Category> GetCategories()
    {
        List<Category> currentCategories = new List<Category>();

        DbCommand comm = GenericDataAccess.CreateTextCommand();
        comm.CommandText = "SELECT Id, Name FROM Categories Order By Name";
        DataTable table = GenericDataAccess.ExecuteSelectCommand(comm);

        foreach (DataRow row in table.Rows)
        {
            Category cat = new Category();
            cat.Id = int.Parse(row["Id"].ToString());
            cat.Name = row["Name"].ToString();
            currentCategories.Add(cat);
        }
        return currentCategories;
    }
}

This is what I have done so hope this helps. Not sure if it is the right way to do it but it works for what we needed it for.

This generates a new object, I need the code to reference the objectCategory cat = new Category();Almost likeCategory cat = (list<Category>)tables.RowsBut that does not work
Coppermill
with that you can map which attributes you want to use. that's a bigger approach but not bad
Junior Mayhé
+1  A: 

Hey Coppermil

I have another approach you might take a look. It's a helper method. Create a custom class file named CollectionHelper:

    public static IList<T> ConvertTo<T>(DataTable table)
    {
        if (table == null)
            return null;

        List<DataRow> rows = new List<DataRow>();

        foreach (DataRow row in table.Rows)
            rows.Add(row);

        return ConvertTo<T>(rows);
    }

Imagine you want to get a list of costumers. Now you'll had the following caller:

List<Customer> myList = (List<Customer>)CollectionHelper.ConvertTo<Customer>(table);

The attributes you have in your DataTable must match your Customer class (fields like Name, Address, Telephone).

I hope it helps!

For who are willing to know why to use lists instead of DataTables: link text

Junior Mayhé
stackoverflow only allow me to give you one point, 2++
Saif Khan
can this conversion be done with a datareader?
Saif Khan
I was wandering if it's posible to avoid this "must match" for class attributes and datatable fields!
Junior Mayhé