Вам в комментариях дали правильное решение, его может хватить для большинства задач. Давайте я вам его продемонстрирую.
Допустим у нас есть класс человека:
public partial class User : ObservableObject
{
public int Id { get; init; }
[ObservableProperty]
private string _name;
[ObservableProperty]
private int _age;
}
Так, как вы знакомы с CommunityToolkit, то я тут его использовал. Это простой класс, который содержит в себе некие свойства о человеке, где все свойства кроме ID вызывают INotifyPropertyChanged
, это сделано для того, чтоб мы могли отслеживать в дальнейшем любые изменения произведенные с объектом где либо.
Имея класс, мы теперь должны сделать DbContext
, я сделаю что-то такое:
public class AppContext : DbContext
{
public DbSet<User> Users { get; set; }
public AppContext()
{
Database.EnsureCreated();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=AppDB.db");
}
}
Ключевое тут DbSet
- так мы задаем таблицу, которая будет (или уже есть) в базе, все остальное, это "для рабочего примера" так сказать, чтоб не возиться с миграциями и прочим. EnsureCreated()
проверит наличие базы, и создаст в ней все необходимое (по хорошему использовать миграции). Ну а UseSqlite()
- у меня SQLite база данных, так я ее подключаю, у вас может быть что-то другое, и может быть за пределами данного класса, это уже не столь важно.
С подключением и прочим покончили, осталось дело за малым, привязка. Я сейчас не буду вдаваться в MVVM, я просто покажу вам простейший пример реализации подобного, а в конце поговорим немного про MVVM. Я напишу в классе окна такой код:
public partial class MainWindow : Window
{
public BindingList<User> Users { get; set; }
private readonly AppContext _appContext;
public MainWindow()
{
InitializeComponent();
DataContext = this;
_appContext = new AppContext();
_appContext.Users.Load();
Users = _appContext.Users.Local.ToBindingList();
Users.ListChanged += this.UsersListChanged;
}
private void UsersListChanged(object sender, ListChangedEventArgs e)
=> _appContext.SaveChanges();
}
new AppContext();
- мы создаем экземпляр нашего DbContext
созданного ранее, и сохраняем его в некую переменную чтобы иметь доступ в различных методах к нему.
Load()
- Загружаю необходимые мне данные из базы в память. Если нужны конкретные данные, то перед вызовом метода фильтруем данные, например так: Users.Where(x=>x.Age > 15).Load();
.
ToBindingList()
- Мы просим EF отдать нам отслеживаемую коллекцию, которую мы заносим в локальное публичное свойство для привязки. BindingList
тут для того, чтоб отслеживать изменения самого объекта, если это не важно, и нам нужны лишь добавления/удаления, то можем смело использовать ObservableCollection
, а из класса убрать INPC.
ListChanged
- Подписываемся на все изменения коллекции, и по этому событию просим базу сохранить все данные.
Осталось дело за малым, XAML, в нем нам понадобиться лишь одна строка:
<DataGrid ItemsSource="{Binding Users}" />
Этого достаточно для того, чтоб понять всю работу.
Запускаем, и видим перед собой такое:

Нажимаем на любое пустое поле, заполняем все данные, после заполнения скидываем фокус со строки нажатием, например, на название столбца (чтоб это поведение изменить, попробуйте создать столбцы самостоятельно и у привязки задать соответствующий тип обновления), получаем в итоге такое:

Как видите, ID у нас 1, его задала нам база, открываем базу, и действительно, там есть нужная нам запись:

Все, мы тесно связали базу с нашим View, без лишней логики теперь идет автоматическое отслеживание любых изменений, будь то добавление, удаление, редактирование, и т.д.
Ну а теперь про тонкости:
MVVM - этот подход подразумевает, что вы делите все на мало связанные друг с другом слои, где UI не знает ничего про Model и ViewModel; Model аналогично не знаем про View и ViewModel; ну а ViewModel это связующий слой, который инициализирует Model, получает из него нужные данные, на основе их подготавливает данные для привязок, обрабатывает UI запросы, на основе которых данные отправляются обратно в Model. Вот собственно User
, AppContext
- это модель, все, что внутри MainWindow
(кроме DataContext
) это ViewModel, все как бы просто, но есть одна маленькая проблема, класс User
это по сути DTO объект, то есть, не желательно его так напрямую использовать, лучше создать ViewModel обертку, в котором будут только необходимые данные для вывода на экран, а дальше уже делать ручное обновление данных, где по VM событию обновления данных, мы берем измененный VM класс человека, и отправляем в Model (базу) нужные изменения, также и наоборот, мы берем из базы данные, и на их основе заполняем VM коллекцию данными, конвертируя DTO в VM объект человека.
Для того, чтоб DataGrid
давал вам возможность добавлять новые данные (пустая строка снизу), тот класс, что он должен создать (в моем случае User
) должен быть с пустым конструктором.
_appContext.Dispose()
- EF довольно умный, и сам удалит ненужное за вас, не использовать Dispose
в данном контексте является нормальной практикой, но вас замучает правда студия предупреждениями) Вообще, я вам советую познать IoC контейнеры, которые за вас будут руководить сроком жизни объекта.
Собственно все, удачи!