Once your DataTable has been populated use the following to create a list.
public static class GenericExtensions
{
public static List<TSource> ToList<TSource>(this DataTable table) where TSource : new()
{
List<TSource> list = new List<TSource>();
var typeProperties = typeof(TSource).GetProperties().Select(propertyInfo => new
{
PropertyInfo = propertyInfo,
Type = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType
}).ToList();
foreach (var row in table.Rows.Cast<DataRow>())
{
TSource current = new TSource();
foreach (var typeProperty in typeProperties)
{
if (!table.Columns.Contains(typeProperty.PropertyInfo.Name))
{
continue;
}
object value = row[typeProperty.PropertyInfo.Name];
object safeValue = value is null || DBNull.Value.Equals(value) ?
null :
Convert.ChangeType(value, typeProperty.Type);
typeProperty.PropertyInfo.SetValue(current, safeValue, null);
}
list.Add(current);
}
return list;
}
}
Example class used for the above which implements change notification as we lose that which a DataTable has.
public class Employee : INotifyPropertyChanged
{
private int _id;
private string _firstName;
private string _lastName;
private DateTime _hiredDate;
public int Id
{
get => _id;
set
{
if (value == _id) return;
_id = value;
OnPropertyChanged();
}
}
public string FirstName
{
get => _firstName;
set
{
if (value == _firstName) return;
_firstName = value;
OnPropertyChanged();
}
}
public string LastName
{
get => _lastName;
set
{
if (value == _lastName) return;
_lastName = value;
OnPropertyChanged();
}
}
public DateTime HiredDate
{
get => _hiredDate;
set
{
if (value.Equals(_hiredDate)) return;
_hiredDate = value;
OnPropertyChanged();
}
}
public override string ToString() => $"{Id,-5}";
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then in a class load the data
public static List<Employee> ReadEmployees()
{
DataTable dt = new DataTable();
using (var cn = new SqlConnection(ConnectionString))
{
using (var cmd = new SqlCommand() {Connection = cn})
{
cmd.CommandText = "SELECT id, FirstName, LastName, HiredDate FROM dbo.employee;";
cn.Open();
dt.Load(cmd.ExecuteReader());
}
}
return dt.ToList<Employee>();
}
Example form code
public partial class Form2 : Form
{
private BindingList<Employee> _bindingList;
private readonly BindingSource _bindingSource = new BindingSource();
public Form2()
{
InitializeComponent();
_bindingList = new BindingList<Employee>(EmployeeOperations.ReadEmployees());
_bindingSource.DataSource = _bindingList;
dataGridView1.DataSource = _bindingSource;
dataGridView1.Columns["id"].Visible = false;
}
private void CurrentButton_Click(object sender, EventArgs e)
{
Employee employee = _bindingList[_bindingSource.Position];
}
private void AllButton_Click(object sender, EventArgs e)
{
foreach (var employee in _bindingList)
{
Console.WriteLine(employee.FirstName);
}
}
}
Is there an easier way? Sure, use Entity Framework Core.