When working with Entity Framework viewed in a DataGridView there are things to consider
- By default sorting is not possible unless using a custom BindingList or with EF Core 5 there is ObservableCollectionExtensions.ToBindingList<T> which provides out of the box sorting
- Without implementing INotifyPropertyChanged for each model you want to get changes for its difficult to react to changes.
With that said, check out my GitHub repository which provides a basic yet robust easy to following code sample for working with EF Core, DataGridView and input controls.
- Uses SQL-Server with a script to create the database/table, if not using SQL-Server the same logic will work
- Take time to review the initial page describing the repository
- As this is basic to allow ease of learning I excluded things like find (which has several forms), delete which is easy.
Tip At the root of the repository, type a period character which will open the repository in the web version of VS-Code
Peek at some code
amespace FrontendApplication
{
public partial class Form3 : Form
{
private readonly BindingSource _bindingSource = new();
public Form3()
{
InitializeComponent();
dataGridView1.AutoGenerateColumns = false;
Shown += OnShown;
}
/// <summary>
/// Initial run will have zero records
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void OnShown(object sender, EventArgs e)
{
BindingList<Person> peopleLocalList = await DataOperations.PeopleLocal();
_bindingSource.DataSource = peopleLocalList;
dataGridView1.DataSource = _bindingSource;
AddButton.Enabled = true;
SaveButton.Enabled = true;
DebugViewButton.Enabled = true;
CurrentButton.Enabled = true;
}
private void AddButton_Click(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(FirstNameTextBox.Text) && !string.IsNullOrWhiteSpace(LastNameTextBox.Text))
{
_bindingSource.AddPersonFromBindingSource(new Person()
{
FirstName = FirstNameTextBox.Text,
LastName = LastNameTextBox.Text
});
_bindingSource.MoveLast();
}
else
{
ErrorDialog("Requires first and last name!");
}
}
private void SaveButton_Click(object sender, EventArgs e)
{
var changes = DataOperations.Show();
if (!string.IsNullOrWhiteSpace(changes))
{
Debug.WriteLine(changes);
}
Debug.WriteLine(DataOperations.Context.SaveChanges());
}
private void DebugViewButton_Click(object sender, EventArgs e)
{
var changes = DataOperations.Show();
if (!string.IsNullOrWhiteSpace(changes))
{
Debug.WriteLine(changes);
}
}
/// <summary>
/// Example use extension method
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CurrentButton_Click(object sender, EventArgs e)
{
InformationDialog($"{_bindingSource.Fullname()}", "Current");
}
}
}
Backend code
namespace FrontendApplication.Classes
{
public class DataOperations
{
public static BaseContext Context = new ();
public static async Task<List<Person>> People()
{
return await Task.Run(async () => await Context.Person.ToListAsync());
}
public static async Task<BindingList<Person>> PeopleLocal()
{
return await Task.Run(async () =>
{
await Context.Person.LoadAsync();
return Context.Person.Local.ToBindingList();
});
}
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();
}
/// <summary>
/// Get changes for any property changed as in
/// this case changes are being tracked
/// </summary>
/// <returns>Changed properties for single Person</returns>
/// <remarks>Generic names are used in the returning object</remarks>
public static List<Tuple<string, object, object>> GetChanges()
{
var changes = new List<Tuple<string, object, object>>();
var person = Context.Person.FirstOrDefault();
person.FirstName += " Changes";
var entry = Context.Entry(person);
foreach (var property in entry.CurrentValues.Properties)
{
var propEntry = entry.Property(property.Name);
if (propEntry.IsModified)
{
changes.Add(new Tuple<string, object, object>(
property.Name,
propEntry.OriginalValue,
propEntry.CurrentValue));
}
}
return changes;
}
}
}