Winforms привязывает данные к бизнес-объектам в многопоточном сценарии без InvokeRequired? - PullRequest
1 голос
/ 01 августа 2010

Например, у меня есть бизнес-объект Person:

class Person : INotifyPropertyChanged
{
    string Name { get; set; }
    DateTime DateOfBirth { get; set; }
}
// ^ abbreviated for better legibility; implementation would be trivial

И у меня есть некоторые элементы управления Winforms, связанные с данными для объекта этого класса:

Person somePerson = ...;
nameTextBox.DataBindings.Add("Text", somePerson, "Name");
dobDatePicker.DataBindings.Add("Value", somePerson, "DateOfBirth");

Теперь я делаю изменения в somePerson, и благодаря внедрению INotifyPropertyChanged эти изменения отражаются в пользовательском интерфейсе.Пока все хорошо.

Теперь к моей проблеме: Если я внесу изменения в somePerson в рабочем потоке (т.е. не в потоке пользовательского интерфейса), например, потому что я загружаюданные из БД в качестве фоновой операции, это может вызвать исключения, поскольку привязка данных пытается обновить элементы управления, что разрешено только в потоке пользовательского интерфейса.

Это означает, что мне нужно вызвать InvokeRequiredна элементе пользовательского интерфейса, чтобы увидеть, разрешено ли мне обновлять бизнес-объект - что выглядит как нарушение логического уровня приложения .

В идеале, я хочу иметь возможность изменять свойбизнес-объекты, не заботясь о том, привязаны ли они к пользовательскому интерфейсу или нет.Это как-то возможно с привязкой данных Winforms или нет?

1 Ответ

3 голосов
/ 01 августа 2010

Это не отвечает на ваш вопрос, но я стараюсь, насколько это возможно, избежать этой проблемы.

Из-за наличия привязки данных я проверяю, что единственным кодом, который может обновлять бизнес-объекты, является код, выполняющийся в потоке GUI.

Для асинхронных операций я использую шаблон:

  1. Отправка работы в фоновый режим: Запуск асинхронной операции, скажем, элемента пула потоков, из потока GUI. Передавать в пул потоков только простые типы данных и получать обратно только простые типы данных. Если бизнес-объекты используются пулом потоков, убедитесь, что это либо совершенно новые (еще не привязанные к данным), либо клоны оригиналов (чтобы избежать одновременного доступа)
  2. Выполните работу и получите результат: Выполните асинхронную операцию в фоновом потоке. Код фонового потока будет владеть «безопасными» объектами, предоставленными ему GUI; он не будет взаимодействовать с остальной частью приложения.
  3. Распаковать результат в графическом интерфейсе: Когда асинхронная операция завершена, она вызывает событие «Я завершен» в потоке графического интерфейса. В ответ поток GUI может распаковать любые результаты фоновой операции и объединить их обратно с основными бизнес-объектами, будучи уверенными в том, что он не будет иметь дело с одновременным доступом.

Я могу порекомендовать класс System.Threading.Tasks.Task в качестве абстракции для большинства вышеперечисленных шагов. Он новый в .NET 4.0, но также доступен в виде отдельной загрузки для приложений .NET 3.5.

Шаги (1) и (2) - это то, что класс Task делает без какой-либо настройки. Вы можете достичь (3), создав отдельную Task из фонового потока и указав TaskScheduler.FromCurrentSynchronizationContext в качестве планировщика. (Вам нужно будет вызвать FromCurrentSynchronizationContext из потока GUI, на шаге (1), а не из фонового потока.)

...