Introduction
Working with DataTable objects is common when dealing with database operations, particularly in legacy systems or when using ADO.NET. However, for modern C# applications, it is often more convenient and type-safe to work with a list of strongly-typed objects instead of rows and columns. This guide will show you how to convert a DataTable to a List<T> in C# efficiently.
By the end of this article, you'll understand.
- Why converting to a list is beneficial.
- Different methods to perform the conversion.
- A generic, reusable approach.
Let's dive in!
Why Convert a DataTable to a List of Objects?
Using a strongly-typed list instead of a generic DataTable provides several benefits.
- Type safety: Compile-time checking instead of runtime errors.
- Intellisense support: Easier and faster coding with autocomplete.
- LINQ queries: Perform powerful and expressive queries over the data.
- Cleaner code: Avoid hardcoded column names or index-based access.
When working in modern C# applications (like ASP.NET MVC, WebAPI, or Blazor), using strongly-typed models greatly improves maintainability and reduces bugs.
Method 1. Manual Conversion
The most basic way to convert a DataTable is by manually looping through its rows and creating objects.
Example
public List<User> ConvertDataTableToUserList(DataTable table)
{
var users = new List<User>();
foreach (DataRow row in table.Rows)
{
var user = new User
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
};
users.Add(user);
}
return users;
}
Here, User is a simple POCO class.
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
Explanation
- We loop through each row of the DataTable.
- For each row, we manually map fields to properties of the User object.
- This method is straightforward but not very scalable for large models or changing schemas.
Method 2. Using Reflection (Generic and Reusable)
Reflection allows a more dynamic and reusable way to map columns to properties automatically.
Generic Converter Method
public static List<T> ConvertDataTable<T>(DataTable table) where T : new()
{
List<T> data = new List<T>();
foreach (DataRow row in table.Rows)
{
T item = new T();
foreach (var prop in typeof(T).GetProperties())
{
if (table.Columns.Contains(prop.Name) && row[prop.Name] != DBNull.Value)
{
prop.SetValue(item, Convert.ChangeType(row[prop.Name], prop.PropertyType));
}
}
data.Add(item);
}
return data;
}
Usage Example
DataTable table = GetUsersDataTable(); // Your method to get a DataTable
List<User> users = ConvertDataTable<User>(table);
Explanation
- The method uses reflection to find matching property names between the class and the DataTable columns.
- Convert.ChangeType ensures that the types are matched properly.
- This makes the method highly reusable for any type.
Caution: Reflection can have a small performance cost. If you have a very large dataset, consider caching property info for optimization.
Bonus: LINQ Approach (when possible)
If you prefer a more LINQ-focused syntax (still using some manual mapping), you can do.
var users = (from DataRow row in table.Rows
select new User
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
}).ToList();
Explanation
- This is essentially a one-liner version of the manual method.
- It’s more concise and readable for smaller projects.
Best Practices
- Ensure column names match property names exactly (case-sensitive).
- Handle DBNull values properly to avoid exceptions.
- For performance-critical applications, avoid reflection on very large datasets or use compiled expressions.
- Use validation or error handling when mapping data, if possible.
Conclusion
Converting a DataTable to a List<T> is a common requirement in C# projects. Whether you do it manually, via reflection, or using LINQ, each approach has its trade-offs. Reflection offers the most reusable pattern, but it can be slower for extremely large datasets. In professional applications, it's advisable to create utilities or helper libraries for these conversions. Once you have a solid method in place, working with clean, strongly-typed objects becomes easy and efficient!