开发者

Detect if single entity needs saving in EF4 and WPF

I am struggling with detecting whether an entity in EF4 has changes that need saving.

After a few years away from .NET rusty is definitely the word I'd use to describe where I am right now. I am trying to learn to use EF4 and WPF while reacquainting myself with .NET. I've followed a number of tutorials on Drag & Drop Databinding with the Entity Framework and WPF and built an app with a few Windows that is getting my knowledge up little by little.

I am using the simplest part of my Model for my training exercises, The model has entities: Network and Laboratory, there is a many-to-many link between Networks and Labs, namely NetworkLabs, the relationship is not particularly important right now as I am still at the very basics.

I have a window that displays a list of Networks in a listbox, with a DataGrid next to it showing the Laboratories in the Network. I was able to do that fairly easily following the tutorials and I ended up with code like:

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

That window is for viewing only, the user can click an edit button to edit the selected network. That second window is where I am hitting problems, on that window I dragged the network entity over from the Data Sources window to create a details screen (a grid with labels and textboxes in the rows and columns). I ended up with code like:

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

And that works fine for showing the network that was selected on the previous screen, I put a Save button on a toolbar which simply calls

m_db.SaveChanges()

To save the changes and that works fine as well. My problem comes when catering for when the user edits the data and closes the window, I want to detect whether the current network needs saving back to the database so I can prompt the user but I don't know how to get hold of the network to check.

I suspect it is something to do with code like:

     Dim networkViewSource As CollectionViewSource = CType(Me.FindResource("UKNEQASEntitiesNetworksViewSource"), CollectionViewSource)
     Dim entry As ObjectStateEntry = m_db.ObjectStateManager.GetObjectStateEntry(....)

but I don't know how to get the network to pass to GetObjectStateEntry.

On my previous list screen I was able to get the selected network by getting the SelectedItem from the listbox but I can't find anything that would help me on my single entry window.

Am I going about this the right way? For the single entry edit screen I am still using CollectionViewSource like I did for the list screen, is that the best way or is there something for single entities?

I have been looking for lots of tutorials and the majority I find are all about displaying the data for editing in DataGrids which is not what I am looking for. I am struggling to find any help on making screens for editing single entities so don't know how to pick up a reference to the entity the user is editing.

Any help is greatly appr开发者_运维技巧eciated as I am a novice at this EF and XAML lark.


You are looking at it from the wrong perspective. Several things that you need to consider when you start using EF:

  • You hardly need to look at ObjectStateManager, this is something EF is supposed to do for you. For example if you want to close your Window and save everything, just call context.SaveChanges() and let EF to the work of finding out what needs saving and what not. There are cases where you need to use the ObjectStateManager, but these are specific and usually associated with very custom things.

  • You are not seeing many examples, because people that are building WPF and Silverlight applications aren't connected to Data Base engines, instead they connect to one or more layers of Web Services and interact with them. When you do this, the first thing it happens is that you no longer work with the "normal" EF entities, these weren't made to be serialized. You will work with either POCO Entities of Self Tracking Entities.

  • If you are using the normal Entities, they are designed to allways work attached to a context (in your case it would be: UKNEQASEntities), this means that if you attempt to use this in a client application you will want to make sure you allways use the same context, so either have a reference to it on a Singleton class or inject it in a Dependency Injection container. If you use multiple contexts you will come across a bunch of problems related with the EntityKeys which are given to objects in relation to the context they exist in (consider the context a memory copy of your Data Base, this isn't true in practice, it works more like an interface that you use to auto-generate sql queries against your Data Base.

In the end, if you want to build a good WPF client application you need to also use:

  • Dependency Injection container: either MEF (which is deployed on a .Net deployment) or Unity (which is part of the Enterprise Library).
  • EF4 Self Tracking Entities (I personally perfer these over POCO objects, because you get a lot of powerfull functionality for free).

You can also poke me directly if you need more specific help, I've been working with this stuff for a while now, in professional development teams on medium to large scale projects. However I have never worked in Visual Basic, so I have a bit of pain when I have to read VB code!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜