none
LINQ to SQL databinding woes with Refresh() and ResolveAll() RRS feed

  • Question

  • I've written an app where many UI controls are bound to LINQ to SQL entities using DataSourceUpdateMode.OnPropertyChanged. In this mode, the controls depend on the entities to fire the proper events when a bound property changes, or the UI will no longer accurately reflect the bound object's current state. If you're designing an object for use as a datasource for .NET databinding, you would either implement a <PropertyName>Changed event for every bindable property, or implement INotifyPropertyChanged and fire the PropertyChanged event. The entity objects generated by LINQ to SQL all implement INotifyPropertChanged, and their implementation works great with the OnPropertyChanged databinding mode in all instances except when you call Refresh() or ChangeConflicts.ResolveAll() on the DataContext, which seems like a critical oversight.

     

    When you use ResolveAll() or Refresh(), you must provide a member of the RefreshMode enum to determine how to treat the target entity. From the documentation, the members are:

     

    KeepCurrentValues: Forces the Refresh method to swap the original value with the values retrieved from the database. No current value is modified.


    KeepChanges: Forces the Refresh method to keep the current value that has been changed, but updates the other values with the database values


    OverwriteCurrentValues: Forces the Refresh method to override all the current values with the values from the database.

     

    To me, these swap/keep/override behavior descriptions imply that something is going to happen to the target entity--something that should invoke the PropertyChanged event when the expected behavior occurs. Instead, what I'm observing by stepping through the code is that LINQ to SQL clones the target entity during these operations, applies the expected property modifications to the clone, then removes the original entity in the model and replaces it with the clone. This effectively disables databinding to the entity for which you just attempted a refresh or conflict resolution. Am I doing something wrong? If not, how did anyone on the LINQ to SQL team think it was a better idea to orphan entities they must assume are in use, rather than just modifying the original and utilizing the PropertyChanged event during these circumstances?

    Wednesday, March 19, 2008 7:46 AM

All replies

  • A somewhat related issue:

     

    I have a database where I try to control the access of certain users upon login. Basically, admins get full toolbars and functionality, whereas users get no toolbars, no database window possibilites, NOTHING. Now, all this would be well and good if it actually worked.

     

    What does happen is that these settings don't come into effect until the next time someone logs in - basically the next time the database is opened (something about persistence??). So in practicality, if I'm an admin, I have to log in TWICE for the changes to take effect, AND (a worse situation) a user logging in after an admin would have full admin access until they log off and log in for the second time.

     

    Back to the thread issue - even a dbs.Properties.Refresh doesn't do the trick because of the whole "persistent" database issue. If anyone can help me out, or point me to any helpful thread or community, it would be much appreciated.

     

    ikos

    Tuesday, March 25, 2008 2:46 PM
  • Are you sure that the objects you have after the refersh are different objects than you had before?  I don't think that is the intent of the code, nor does it seem to match my own experience of using Refresh.  I.e. if you Refresh an object, it should still be the same object, just with new data.

     

    While it does seems plausible that the PropertyChanged events don't fire on Refresh, it really out to be the same object still, after the refresh.

     

    Is there some way that you can just to an accross-the-board refresh of your UI, after refershing the object?  E.g. by looping through all the bindings, asking them to refresh themselves?

     

    Wednesday, March 26, 2008 7:45 PM
  • Hi, are you still experiencing this issue or did you find a suitable solution?

     

    Thanks,

     

    [)amien

    Tuesday, June 10, 2008 5:48 PM
    Moderator
  • I suspect this issue is because Linq to SQL does not really work with WinForms databinding in a sane way.

     

    I really fuzzy on this stuff but as far as I can figure the refresh method is really only there to resolve concurrency conflicts in the datacontext after submitchanges throws an exception.

     

    The datacontext will always be stale with respect to other users changes of the underlying SQL db.   If you requery using Linq you just get the same old object back (even though it still sends out the same sql select just to pick up new objects; bizarre).  If you want fresh data you're meant to throw away the old datacontext and start a new one at the drop of a hat, it's all supposed to light and stateless and webby.  Although you can use the refresh method of the datacontext with a single object, or even a collection of objects, for a collection it's a hack; it causes a sql select statement to be executed for every single object (instead of the one select statement that fills all the objects en masse the first time around), this is obviously no good for a winforms app with a datagrid bound to thousands of results.  So you are intended to throw away the old datacontext and refill the datagrid/list with a new context.

     

    Trouble is if you have an even slightly complex winforms databinding senario that's about as much use as a chocolate teapot.  So, for example, if you have an outlook style app where you have a list of records and you double click a line to get the record details in a form for editing and if all you're forms are hanging off the same datacontext so you can have all the nice INotifyPropertyChanged stuff keeping the all the inspector forms and the grid contents in sync it seems fine until you realise you need to requery the database, since then you'll have to throw the datacontext out and lose all the uncommited changes in the inspectors.

     

    Of course all we would need is an option to ask the query/projection engine thingy to refill the existing objects with new data from the store as long as they haven't changed client side since the last execution of the query.  Then have the objects raise all the INotifyPropertyChanged etc. so the GUI gets the picture.  Even better on a background thread (with the UI calls marshalled).  But that isn't there so we're all sunk.  I don't know if the Entity Framework does any better.

     

    I guess all this good stuff was done for Scott Gu types and not us WinForms guys...

     

    ChrisE

    Wednesday, June 11, 2008 11:00 PM
  • I suspect that private variables are involved in the Refresh() operation, not the public properties (where the INotifyPropertyChanged event is raised). You can check this by looking at the <Column(Storage:="_PrivateVariableName")> attribute of your entity's public property.
    Wednesday, September 10, 2008 6:05 PM
  • Any solution? DataContext.Refresh does not refresh the bounded UI?
    Sunday, February 7, 2010 10:39 AM