karenpayneoregon / efcore-datagridview-ToBindingList

How to work with a DataGridView with EF Core 5

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WinForms DataGridView with EF Core 5

⚠️ Note

The inital run of the application will not have any records, use the add button to add data.


Typically when a developer new to working with database data in a Windows Form application tend to add rows directly to the DataGridView rather than set the DataGridView DataSource property which means more code rather than setting the DataGridView DataSource property using a DataSet or DataTable.

When setting the DataSource property for a DataGridView to a DataTable or DataSet then perform save operations all works as expected. This is not the case when working with Entity Framework Core 5 (EF Core 5).

Basic mistake where adding a new record (as shown in Form1) will not display the new record in the DataGridView.

private async void OnShown(object sender, EventArgs e)
{
    var list =  await DataOperations.People();
    dataGridView1.DataSource = list;
}

Next level is to use a BindingList<T>, in this case BindingList<Person>.

The key is ToBindingList() extension method.

public static async Task<BindingList<Person>> PeopleLocal()
{
    return await Task.Run(async () =>
    {
        await Context.Person.LoadAsync();
        return  Context.Person.Local.ToBindingList();
    });
}

In Form2 data is load

private async void OnShown(object sender, EventArgs e)
{
    BindingList<Person> peopleLocalList = await DataOperations.PeopleLocal();
    dataGridView1.DataSource = peopleLocalList;
}

Now when adding a new Person record the record is displayed in the DataGridView.

Even better is to incorporate a BindingSource which provides navigation ability, useful methods and events.

private readonly BindingSource _bindingSource = new BindingSource();

private async void OnShown(object sender, EventArgs e)
{
    BindingList<Person> peopleLocalList = await DataOperations.PeopleLocal();
    _bindingSource.DataSource = peopleLocalList;

    dataGridView1.DataSource = _bindingSource;
}

Evaluating the three approaches

  • Form1 just does not work
  • Form2 we need to touch the DataGridView once loaded to get at loaded data
  • Form3 Set the DataGridView.DataSource and no need to touch the DataGridView once loaded to access data.
    • Check out 👉 ChangeTracker.DebugView.LongView

Evaluating local changes

In DataOperations class, Show method provides access to the State of each record.

public static string Show()
{
    StringBuilder builder = new ();
    foreach (var person in Context.Person.Local)
    {
        if (Context.Entry(person).State != EntityState.Unchanged)
        {
            builder.AppendLine($"{person.Id} {person.FirstName} {person.LastName} {Context.Entry(person).State}");
        }
    }

    return builder.ToString();
}

Lessons learned

What can be learned is not to work directly with a DataGridView for displaying database data using EF Core 5 or higher.

Novice developers tend to want the easiest way to code a solution which is great while when they don�t understand and/or think ahead will a) write a good deal more code then required b) seek help and in many cases unwilling to accept, in this case using a BindingList with a BindingSource.

So looking at the big picture past working with a DataGridView and EF Core is to think ahead for other coding task. Even better, take time when appropriate after reading documentation write unit test to validate that what you think the code should do actually does.

Running code

  • Requires
    • Visual Studio 2019 or higher
    • SQL-Server Express edition or higher
    • Run createPersonTable.sql script
    • In Program.cs, set the startup form to Form1, run, try Form2, run the Form3 and run

See also

Edit

Added Form4 to respond to a form question.

About

How to work with a DataGridView with EF Core 5


Languages

Language:C# 97.6%Language:TSQL 2.4%