#wpf #xaml #entity-framework-4 #drag-and-drop #binding
#wpf #xaml #entity-framework-4 #перетаскивание #обязательный #привязка
Вопрос:
Я пытаюсь определить, есть ли у объекта в EF4 изменения, которые нуждаются в сохранении.
После нескольких лет отсутствия .NET rusty — определенно то слово, которое я бы использовал, чтобы описать, где я нахожусь прямо сейчас. Я пытаюсь научиться использовать EF4 и WPF, одновременно заново знакомясь с .NET. Я следовал ряду руководств по привязке данных перетаскиванием с помощью Entity Framework и WPF и создал приложение с несколькими окнами, которое постепенно расширяет мои знания.
Я использую простейшую часть моей Модели для своих тренировочных упражнений, в модели есть объекты: Сеть и лаборатория, между сетями и лабораториями существует связь «многие ко многим», а именно NetworkLabs, взаимосвязь сейчас не особенно важна, поскольку я все еще нахожусь в самых основах.
У меня есть окно, в котором отображается список Сетей в виде списка, а рядом с ним — Таблица данных, показывающая Лаборатории в Сети. Я смог сделать это довольно легко, следуя руководствам, и в итоге у меня получился код типа:
Public Class NetworkListWindow
Private Function GetNetworksQuery(entities As UKNEQASEntities) As ObjectQuery(Of Network)
Dim networksQuery As ObjectQuery(Of Network) = entities.Networks
' Update the query to include NetworkLabs data in Networks.
networksQuery = networksQuery.Include("NetworkLabs")
' Returns an ObjectQuery
Return networksQuery
End Function
Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
Dim entities As UKNEQASEntities = New UKNEQASEntities()
' Load data into Networks.
Dim networksViewSource As CollectionViewSource = CType(Me.FindResource("UKNEQASEntitiesNetworksViewSource"), CollectionViewSource)
Dim networksQuery As ObjectQuery(Of Network) = GetNetworksQuery(entities)
networksViewSource.Source = networksQuery.Execute(MergeOption.AppendOnly)
End Sub
End Class
Это окно предназначено только для просмотра, пользователь может нажать кнопку редактирования, чтобы отредактировать выбранную сеть. Это второе окно, в котором я сталкиваюсь с проблемами, в этом окне я перетащил сетевой объект из окна Источники данных, чтобы создать экран сведений (сетка с метками и текстовыми полями в строках и столбцах). В итоге я получил код типа:
Public Class NetworkWindow
Private m_id As Integer
Private m_db As New UKNEQASEntities
Public Sub New(id As Integer)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
m_id = id
End Sub
Private Function GetNetworkQuery() As ObjectQuery(Of Network)
Dim networkQuery As ObjectQuery(Of Network) = m_db.Networks
' Update the query to include only the Network we are editing
networkQuery = networkQuery.Where(Function(net) net.Id = m_id)
' Update the query to include NetworkLabs data in Networks.
networkQuery = networkQuery.Include("NetworkLabs")
' Returns an ObjectQuery
Return networkQuery
End Function
Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
' Load data into Networks.
Dim networkViewSource As CollectionViewSource = CType(Me.FindResource("UKNEQASEntitiesNetworksViewSource"), CollectionViewSource)
Dim networksQuery As ObjectQuery(Of Network) = GetNetworkQuery()
networkViewSource.Source = networksQuery.Execute(MergeOption.AppendOnly)
' Get laboratories that are not in any networks
Dim labResult = From laboratory In m_db.Laboratories _
Where _
Not _
(From networklab In m_db.NetworkLabs _
Select networklab.Laboratory.Id).Contains(laboratory.Id) _
Select laboratory
Dim laboratoriesViewSource As CollectionViewSource = CType(Me.FindResource("UKNEQASEntitiesLaboratoriesViewSource"), CollectionViewSource)
laboratoriesViewSource.Source = labResult.ToList
End Sub
End Class
И это отлично работает для отображения сети, которая была выбрана на предыдущем экране, я поместил кнопку Сохранения на панель инструментов, которая просто вызывает
m_db.SaveChanges()
Чтобы сохранить изменения, и это также отлично работает. Моя проблема возникает при обслуживании, когда пользователь редактирует данные и закрывает окно, я хочу определить, нуждается ли текущая сеть в сохранении обратно в базу данных, чтобы я мог запросить пользователя, но я не знаю, как связаться с сетью для проверки.
Я подозреваю, что это как-то связано с кодом типа:
Dim networkViewSource As CollectionViewSource = CType(Me.FindResource("UKNEQASEntitiesNetworksViewSource"), CollectionViewSource)
Dim entry As ObjectStateEntry = m_db.ObjectStateManager.GetObjectStateEntry(....)
но я не знаю, как заставить сеть перейти к GetObjectStateEntry.
На моем предыдущем экране списка я смог получить выбранную сеть, получив SelectedItem из listbox, но я не могу найти ничего, что помогло бы мне в моем окне единого ввода.
Правильно ли я поступаю по этому поводу? Для экрана редактирования единой записи я все еще использую CollectionViewSource, как я делал для экрана списка, это лучший способ или есть что-то для отдельных объектов?
Я искал множество руководств, и большинство из них, которые я нахожу, посвящены отображению данных для редактирования в DataGrids, что не то, что я ищу. Я изо всех сил пытаюсь найти какую-либо помощь по созданию экранов для редактирования отдельных объектов, поэтому не знаю, как получить ссылку на объект, который редактирует пользователь.
Я очень ценю любую помощь, поскольку я новичок в этом EF и XAML.
Ответ №1:
Вы смотрите на это с неправильной точки зрения. Несколько вещей, которые необходимо учитывать при начале использования EF:
-
Вам вряд ли нужно смотреть на ObjectStateManager, это то, что EF должен сделать за вас. Например, если вы хотите закрыть свое окно и сохранить все, просто вызовите context.SaveChanges() и предоставьте EF самому определять, что нуждается в сохранении, а что нет. Бывают случаи, когда вам нужно использовать ObjectStateManager, но они специфичны и обычно связаны с очень нестандартными вещами.
-
Вы видите не так много примеров, потому что люди, создающие приложения WPF и Silverlight, не подключены к механизмам базы данных, вместо этого они подключаются к одному или нескольким уровням веб-сервисов и взаимодействуют с ними. Когда вы делаете это, первое, что происходит, это то, что вы больше не работаете с «обычными» объектами EF, они не были созданы для сериализации. Вы будете работать либо с объектами POCO, либо с объектами самостоятельного отслеживания.
-
Если вы используете обычные объекты, они предназначены для постоянной работы с привязкой к контексту (в вашем случае это было бы: UKNEQASEntities), это означает, что если вы попытаетесь использовать это в клиентском приложении, вам захочется убедиться, что вы всегда используете один и тот же контекст, поэтому либо имейте ссылку на него в одноэлементном классе, либо внедряйте его в контейнер для внедрения зависимостей. Если вы используете несколько контекстов, вы столкнетесь с кучей проблем, связанных с EntityKeys, которые присваиваются объектам в зависимости от контекста, в котором они существуют (рассматривайте контекст как копию вашей базы данных в памяти, на практике это неверно, он работает скорее как интерфейс, который вы используете для автоматической генерации sql-запросов к вашей базе данных.
В конце концов, если вы хотите создать хорошее клиентское приложение WPF, вам также необходимо использовать:
- Контейнер для внедрения зависимостей: либо MEF (который развертывается на .Сетевое развертывание) или Unity (который является частью корпоративной библиотеки).
- Объекты самоконтроля EF4 (лично я предпочитаю их объектам POCO, потому что вы получаете много мощной функциональности бесплатно).
Вы также можете связаться со мной напрямую, если вам нужна более конкретная помощь, я уже некоторое время работаю с этим материалом в профессиональных группах разработки над проектами среднего и крупного масштаба. Однако я никогда не работал в Visual Basic, поэтому мне немного больно, когда мне приходится читать код VB!
Комментарии:
1. Спасибо за ответ, я думаю, что я вступаю в мир боли, следуя примерам MS «EF4 в WPF». В примерах показана привязка пары объектов к спискам / сеткам данных в главном окне, затем показано создание кнопки сохранения для вызова . Сохраните изменения в контексте, а затем остановите. Я пытаюсь создать приложение, в котором есть небольшое количество объектов с довольно большим количеством взаимосвязей между ними, пользователям нужно будет иметь возможность вносить изменения, сохранять их, отменять изменения, отменять что-либо, поэтому я изо всех сил пытаюсь применить все эти вещи при расширении моего подхода к примерам MS.
2. Я поместил свой контекст в одноэлементный класс, как вы предложили, и мое окно редактирования теперь может определять, есть ли изменения, которые нужно сохранить, только для того, чтобы я мог выдать пользователям стандартное приглашение: Вы хотите сохранить изменения? Да вызывает .SaveChanges, отмена останавливает закрытие окна и No ничего не делает, когда оно должно отменить изменения. С контекстом в одноэлементном классе ничего не делать означает, что отредактированная Сеть по-прежнему сохраняет все изменения, которые я внес, когда я возвращаюсь к предыдущему окну. Есть ли простой способ отменить изменения в контексте?