Как обновить пользовательский интерфейс WPF при изменении базы данных

#c# #wpf #multithreading #mvvm #datagrid

#c# #wpf #многопоточность #mvvm #datagrid

Вопрос:

Сначала я пишу приложение на C # / WPF / Entity Framework DB, которое взаимодействует с проприетарным промышленным оборудованием, которое измеряет такие значения, как расход и температура в трубопроводе охлаждающей жидкости. Эти значения часто обновляются в моей базе данных SQL Server с помощью потока, который выполняется в фоновом режиме, а затем отображаются в моем пользовательском интерфейсе.

В настоящее время меня беспокоит то, как я обновляю свой пользовательский интерфейс, чтобы отразить эти изменения. Вот пример того, как я обновляю datagrid:

Я устанавливаю свой datacontext в свою viewmodel и создаю экземпляр потока, который выполняется каждую секунду:

     _DataContext = new ViewModels.SummaryTable_ViewModel();
    this.DataContext = _DataContext;
    UIUpdateThread = new Thread(UIUpdaterThread);
    UIUpdateThread.IsBackground = true;
    UIUpdateThread.Start();       
  

Модель, на которой основана моя сетка, — это IList<>, который выглядит следующим образом:

     private IList<channel> _channel;
    public IList<channel> Channels
    {
        get
        {
            return _channel;
        }
        set
        {
            _channel = value;
            //NotifyPropertyChanged();
            OnPropertyChanged("Channels");
        }
    }
  

Затем каждую секунду мой UIUpdateThread вызывает мой метод FillChannels(), который выглядит следующим образом, затем сетка обновляется на основе уведомления об изменении свойств:

 using (var DTE = new myEntities())
            {
                if (DTE.channels.Any())
                {
                    var q = (from a in DTE.channels
                             where a.setup.CurrentSetup == true
                             orderby a.ChannelNumber
                             select a).ToList();

                    this.Channels = q;
                }
            }
  

Итак, мой вопрос таков: есть ли лучший, более элегантный способ сделать это? Это кажется «неправильным» из-за отсутствия лучшего термина. И у этого есть плохие побочные эффекты, такие как сброс пользовательской сортировки в моей сетке данных каждый раз при запуске потока. Также я думаю, что это нарушает шаблон MVVM, хотя я не уверен.

Например, я думаю, что, если поток, который взаимодействует с моим оборудованием, обновляет объект общего канала каждый раз, когда он опрашивает оборудование на предмет данных, и я просто привязываю к нему свои пользовательские интерфейсы, таким образом, мне не пришлось бы запускать этот поток (или другие потоки в других моих пользовательских интерфейсах, которые делают то же самоевещь), просто обновляйте на основе уведомлений об изменении свойств. Или есть какой-то другой метод, о котором я полностью не знаю? При поиске ответа я увидел упоминания о шаблоне единицы работы, с которым я незнаком, это уместная концепция здесь?

Как вы можете видеть, я не слишком уверен, куда идти дальше, и мне действительно нужна помощь. Извините за стену текста, я просто хотел быть как можно более тщательным. Любая помощь будет с благодарностью принята. Спасибо.

Комментарии:

1. Для начала я бы сделал Channels ObservableCollection . Если элементы добавляются или удаляются, я бы добавил / удалил их вместо воссоздания коллекции. В общем, ваш предпоследний абзац посвящен деньгам: измените существующие вещи, которые вызывают соответствующие уведомления об изменениях. Становится немного запутанным по сравнению с простым переходом в совершенно новое текущее состояние, но если вам нужно это сделать, вам нужно это сделать. PS: Что сказал rory.ap.

2. Спасибо за помощь @EdPlunkett

Ответ №1:

Я думаю, что ваша модель должна быть той, которая уведомляет вашу модель представления о появлении новых данных. Вы должны поместить любой механизм, который выполняет это (например, таймер, который, как я полагаю, вы используете), в модель.

По сути, ваша модель уведомляет вашу модель представления, а ваша модель представления уведомляет представление об изменениях данных. Если вы правильно настроили свое представление и используете XAML, а не code-behinds, все это должно автоматически объединяться через привязки данных между представлением и моделью представления.

Что касается сортировки: это должно быть отдельным свойством вашей модели представления, которое привязано к свойству сортировки в сетке вашего представления. Ваша модель должна поддерживать заданную пользователем сортировку и соответствующим образом применять ее к обновленным данным, прежде чем она уведомит модель представления об изменении данных.

Я не уверен, применима ли здесь единица работы, поскольку вы не говорите о сохранении связанных строк данных в нескольких таблицах и выполнении их как одной транзакции — вы только считываете данные. Я могу ошибаться, поскольку я не так много изучал unit of work.


Чтобы ответить на ваши вопросы в комментариях:

Давайте сделаем шаг назад. Ваша модель представления должна касаться только логики, связанной с представлением связанного представления. В нем не должно быть никакого кода доступа к данным или кода, который выполняет какую-либо бизнес-логику или задачи, связанные с доменом; этот код входит в то, что в совокупности называется «моделью» (простите, если у вас это уже есть, но трудно точно сказать, где находится код, который вы указали в своем вопросе).

Модель может состоять из нескольких классов, которые выполняют разные задачи бизнес-логики или которые представляют ваш домен / объекты POCOS (например, классы, созданные вашей entity framework).

Мне немного неясно, что такое ваш Channel объект и как он отделен от вашего класса сущностей / POCO, то есть модели, сгенерированной EF. Но опять же, ваше взаимодействие с EF, а также обслуживание и логика, связанные с вашими объектами, должны происходить в модели.

Итак, да, идея заключается в том, что вы ищете изменения данных в модели, используя любой механизм, который вы хотите — например, таймер (кстати, есть таймеры, которые выполняются синхронно, поэтому не объединяйте понятие «таймер» с «потоком») или привязку непосредственно к событию «коллекция изменена»в вашей коллекции объектов. При обнаружении изменения вы просто выполняете любую бизнес-логику, необходимую для этого изменения в модели (например, применение преобразований, сортировка, ведение журнала и т.д.), А затем уведомляете модель представления о том, что изменение произошло.

Комментарии:

1. ОК. Прежде всего, спасибо за помощь. Чтобы убедиться, правильно ли я понимаю — какова моя модель в данном случае? Это автоматически сгенерированные модели, созданные Entity Framework? Если да, значит ли это, что мне нужно преобразовать в CodeFirst, чтобы я мог их изменить? Или это объект канала, который у меня есть в моей viewmodel? Идея в том, что я бы поместил в модель поток, который время от времени запрашивает базу данных, как я делаю сейчас, и просто привязываюсь к нему напрямую? Затем мой пользовательский интерфейс обновляется, потому что срабатывает событие CollectionChanged? (Я предполагаю, что это то, что мне понадобится, потому что моя коллекция отредактирована больше, чем добавлена / удалена из

2. Ненавижу вас беспокоить, @rory.ap — Я отредактировал свой вопрос в комментарии выше этого. Не помешало бы немного уточнить, где мне нужно обновлять мою модель / что мы подразумеваем под моделью. Я думаю, что я очень близок к пониманию этого. Продолжайте и пометьте ваш ответ как принятый, хотя потому, что это был единственный ответ, и, похоже, он охватывает то, что мне нужно знать. Я все еще немного смущен из-за отсутствия опыта. В любом случае, еще раз спасибо.

3. Большое, очень большое спасибо, @rory.ap . Итак, я так понимаю, что мне нужно сделать, это создать частичный класс, который расширит мои POCOS, созданные EF, но в этом классе я добавлю свою бизнес-логику и буду отслеживать изменения. Или, может быть, создать совершенно новый класс, не уверен. В любом случае, сейчас я ошибаюсь, потому что моя viewmodel обрабатывает такие вещи, как запросы к БД, сортировка и возврат коллекций, к которым я привязываюсь. Вместо этого я просто создам экземпляр этой модели, свяжусь с ней и буду ждать уведомлений об изменениях. Думаю, я понял. Или у меня, по крайней мере, достаточно, чтобы уйти от того, где я могу собрать это вместе. Еще раз: большое спасибо.

4. @user3900520 — Вы не хотите менять свои EF pocos — оставьте их делать то, что они делают. Создайте отдельный класс для своей бизнес-логики — и даже там, отдельный класс для каждой проблемы бизнес-логики . (Обычно я создаю статические классы, но это зависит от вас). Принцип единой ответственности , возможно, является одним из самых важных принципов разработки программного обеспечения (не верьте мне на слово it…it это один из пяти ТВЕРДЫХ принципов).

5. Понял. Спасибо. Я слышал о принципе единой ответственности, но не читал об этом подробно, поэтому я так и сделаю. Чтобы дать вам некоторое представление — я специализировался в бизнесе, а не в области компьютерных наук, поэтому я изучаю лучшие практики на лету. Я также рассмотрю SOLID. Хорошего дня.