Определить, нуждается ли отдельный объект в сохранении в EF4 и WPF

#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 ничего не делает, когда оно должно отменить изменения. С контекстом в одноэлементном классе ничего не делать означает, что отредактированная Сеть по-прежнему сохраняет все изменения, которые я внес, когда я возвращаюсь к предыдущему окну. Есть ли простой способ отменить изменения в контексте?