Время жизни Datacontext в сценарии привязки WinForm
Это меня давно озадачило. Но я не эксперт. Это немного долго...
У меня есть приложение WinForms с пользовательским интерфейсом в стиле Outlook. То есть на левой панели есть панель, которая позволяет вам выбрать "экран", который является элементом управления WinForms, скажем, экран клиента, а на правой панели появится список клиентов (т.е. контроль). Я называю это интерфейсом проводника. Двойной щелчок по записи вызовет немодальную запись о клиенте в дополнительном окне, так же как вы открываете электронное письмо в Outlook, мы называем это инспектором. Если вы дважды щелкните несколько записей, вы получите несколько инспекторов.
Все это делается с помощью привязки данных. Элемент управления BindingSource имеется в элементе управления списком клиентов, а другой - в инспекторе клиентов. Пользовательский элемент управления сообщает статический DataContext в своем событии загрузки и присваивает результат простого запроса Linq-To-SQL свойству источника данных BindingControl. Если дважды щелкнуть список клиентов, событие ищет запись, преобразует ее в объект клиента Linq-To-SQL и передает ее в конструктор формы инспектора клиентов. Инспектор клиентов получает объект клиента и назначает ему свойство источника данных своего элемента управления BindingSource.
Поскольку элемент управления BindingSource поддерживает IBindingList, содержимое объекта клиента не изменяется до тех пор, пока не будет вызван EndEdit, в этом приложении при нажатии кнопки OK. Поскольку Linq to SQL реализует интерфейс INotifyPropertyChanged, список клиентов затем обновляется. Здорово.
Однако проблема возникает, когда я хочу обновить содержимое списка клиентов, чтобы получать изменения, сделанные другими пользователями, что я хочу делать через регулярные промежутки времени, скажем, каждые 60 секунд. Если у меня есть таймер и я повторяю запрос к тому же текстовому тексту, никакие изменения не будут получены, потому что Linq to SQL не хочет отменять изменения, внесенные в данные, находящиеся под контролем текстового данных. Как ни странно, он выполняет запрос к БД, но отслеживание идентичности означает, что в список добавляются только новые объекты клиентов, возвращенные из БД. Если я создаю другой текстовый текст данных, то все открытые инспекторы клиентов больше не используют тот же текст данных, поэтому любые будущие изменения открытого инспектора клиентов не отражаются в списке клиентов.
В основном данные устарели, потому что я не использую шаблон единиц работы. Так что (если вы все еще со мной), вопросы.
1. Как, черт возьми, я могу заставить модель работы работать в этой ситуации? Достаточно ли просто ASP.NET для использования текстового контекста в области запроса, он недолговечный, но в WinForms?
2. Есть ли другие ORM, которые будут работать лучше в этом сценарии? NHibernate, EF, LLBLGEN и т. Д.
3. Как еще я должен идти об этом?
А также.
4. Если я смогу заставить Linq to SQL работать таким образом, кто-нибудь реализовал IBindingList в частичных классах Linq to SQL, что позволило бы избежать использования элементов управления IBindingSource. (Я не уверен, что должен заботиться об этом).
5. Если я могу заставить Linq to SQL работать таким образом, есть ли способ использовать SQL-уведомления в SQL 2008, чтобы я мог получать уведомления, когда базовый результат запроса изменяется и затем запрашивает, а не опрашивает.
Спасибо!
PS я в курсе что могу использовать
db.Refresh(System.Data.Linq.RefreshMode.KeepChanges, customers)
но это приводит к выполнению запроса к базе данных для каждой записи клиента в списке.
5 ответов
Я собираюсь изложить вашу проблему, чтобы убедиться, что я ее понял.
У вас есть виджет, который представляет список объектов (СПИСОК). Когда вы щелкаете элемент в списке, появляется другой виджет, который позволяет пользователю редактировать объект. Когда пользователь завершил редактирование сущности, его изменения фиксируются в БД и также должны быть отражены в СПИСКЕ сущностей. Периодически система также должна извлекать изменения, внесенные другими пользователями в элементы списка, и обновлять список.
Если это правильно, я оставлю в стороне любые проблемы с параллелизмом двух пользователей, редактирующих одну и ту же сущность, поскольку это, похоже, не является вашей заботой, и я сосредоточусь на том, как организовать пользовательский интерфейс и единицу работы.
Вам необходимо отделить сущности в вашем СПИСКЕ от сущностей, редактируемых вашими инспекторами. Бизнес-процесс, представленный вашими инспекторами, - это ваши единицы работы, по одной единице на каждую организацию. Ваш список не представляет собой единицу работы. Это устаревшее представление или момент совместной работы всех ранее совершенных единиц работы. Вашему LIST даже не нужно иметь дело с вашими сущностями напрямую, он может содержать идентификатор или любой другой способ, которым ваши инспекторы могут получить базовый объект из БД, когда пользователь щелкает по нему. Теперь вы сможете обновлять список в любое время, так как ваши инспекторы вообще не делятся с ним экземплярами.
Чтобы смоделировать для вашего пользователя, что, когда он редактирует сущность через инспектора, и заставить их казаться связанными с одной и той же вещью, у вас есть два варианта.
1) список привязан только к зафиксированным данным в БД. Это легко, когда инспектор сбрасывает локальные изменения обратно в базу данных и выполняет успешную фиксацию, предоставляя инспектору возможность сообщить списку об обновлении.
2) список привязан к зафиксированным данным + локальные не зафиксированные данные. Это немного сложнее, вам нужно выставить в своем списке методы, которые позволят инспектору превзойти данные, возвращаемые из БД, и перезаписать их своими локальными грязными данными.
Возможно, немного старый... Но в отношении пунктов 4/5 обязательно ознакомьтесь с проектом Bindable LINQ на CodePlex. Определенно хороший код, который решает именно вашу проблему.
В настоящее время я пытаюсь реализовать идентичный сценарий в нашем приложении WinForms SmartClient.
Вы пришли к хорошему решению с этим?
В нашем приложении мы предъявляем те же требования к параллельному интерфейсу и обновляем данные из общего источника, однако вместо LinqToSql мы используем службы WCF. Я реализовал нашу собственную карту идентификации и решение для отслеживания изменений.
Очевидно, важно, чтобы изменения, внесенные в вашего клиента в инспекторе, были отражены в других представлениях. Но этот вид создает у пользователя впечатление, что его изменения были сохранены.
Я бы нарисовал линию вокруг единицы работы, чтобы начать, когда вы читаете список клиентов, и закончить, когда изменения пользователя были сохранены. Если вы перечитаете список клиентов и изменений не будет, это новая единица работы. Если есть изменения, вам нужно закрыть и сохранить изменения, внесенные пользователем, прежде чем вы сможете начать новую единицу работы.
Наша проблема в том, что у нас открыто много окон одновременно, и пользователю легко вносить некоторые изменения, а не фиксировать их в бэкэнде. Тогда не очевидно, почему они не могут перезагрузить основной список.
В этом случае мы решили, что должны автоматически сохранять изменения пользователя.
Вы придумали какие-нибудь лучшие решения?
Я попробовал похожую вещь, вот мои два цента.
Я не думаю, что вы можете реализовать здесь единицу работы из-за того, как работает ваш пользовательский интерфейс. Как вы, возможно, уже знаете, LinqToSql DataContext разработан как легкий и недолговечный объект. Это естественно связывается с "единицей работы". В вашем случае фиксация изменений в БД - это одна единица, обновление изменений в БД - это другая единица. Но вы хотите, чтобы один экземпляр DataContext делал оба.
Кроме того, мне любопытно, что должен делать ваш пользовательский интерфейс, когда пользователь редактирует одну запись, а другой пользователь просто фиксирует некоторые изменения в той же записи в БД. Как вы справляетесь с такими конфликтами параллелизма с точки зрения пользовательского интерфейса?
Возможно, вам придется пойти на компромисс в пользовательском интерфейсе. Один из способов сделать это состоит в том, чтобы сделать детальный просмотр клиента двумя режимами "показ" и "редактирование". Показ - просто представление только для чтения с таймером, обновляющимся в некотором интервале. Редактирование - это моментальный снимок, который позволяет пользователям изменять данные, но не имеет представления об обновлениях других людей. В конце концов, когда пользователи фиксируют обновления, пусть оптимистический параллелизм обрабатывает конфликт. Пользователи не могут видеть изменения в реальном времени, когда они редактируют.
Ваша точка № 5 интересна. Мы делали что-то, что не делало ничего, кроме запросов к БД для сбора новейших обновлений, основанных на интервале или некоторых сигналах. Мы назвали это "издательским сервисом". Чтобы это работало, вам нужно иметь столбец метки времени в вашей таблице БД.
С помощью "службы публикации" вы можете получить набор дельты (обновления и новые записи), не используя DataContext в пользовательском контроле. Если вы "объедините" набор дельты с вашим локальным источником данных DataBinding, ваше представление сведений о клиенте должно обновиться. Теперь экземпляр DataContext в пользовательском элементе управления предназначен для обновления. Вы можете позволить пользователю решать, когда совершать. Или вы можете сделать это, когда пользователь покидает строку (во время проверки). Лично я бы сделал последнее, потому что мне не хочется, чтобы DataContext работал в течение непредсказуемого периода времени.
@Andronicus
Посмотрев на десятки ORM, я в настоящее время изучаю коммерческий ORM под названием Genome ( http://www.genom-e.com/).
Кажется, я позволил мне сделать больше из вышеперечисленного, например, более сложное связывание с данными, и записи могут быть остановлены из-за устаревания, но я все еще нахожу свой путь, хотя это. Я дам вам знать, как у меня дела, но я могу быть когда-нибудь.
@Красная собака
Я посмотрел на BindableLinq, и мне действительно это нравится. Но это Linq to Objects и, следовательно, не имеет перевода Linq to SQL, насколько я могу судить. (Если я не ошибаюсь).
Большое спасибо!