locked
DataServiceCollection from query is updating my ViewModel! RRS feed

  • Question

  • Really strange things are happening in my MVVM application. Heres a quick breakdown, I have the following:

    1. A ViewModel with a SelectedEntity property. When this is set I hook up SelectedEntity.PropertyChanged event

    2. A DataGrid containing a collection of Entities, SelectedItem is bound to the above SelectedEntity property

    3. A DataServiceCollection is created by a query to the Data Service and set as the DataGrid.ItemsSource

     

    So everything works as expected except thos. I have already selected an item from the DataGrid and then re-queried the service, if the query returns the same entity that I has previously selected then I get the PropertyChanged event firing, even if no changes have been made to the entity.

    Whilst I am not in danger of loosing data from this scenario it is a problem because I have a Command's CanExecute depending on whether PropertyChanged event is fired. This causes my Save button to enable even though no editing has actually occured.

     

    Anyone got any ideas?

    Regards,

    Neil

     

     

    Monday, August 23, 2010 9:20 PM

Answers

  • What I was trying to say is that it's not a bug. If you set MergeOption.AppendOnly the materialization will effectively skip entities which already exist on the client. So if your query expands some nav properties of already existing entity, the expanded entities will not be added to the nav property (as that would mean changing the already existing entity, which is not allowed by the MergeOption).

    If some of them do have expanded entities and some of them don't, it's hard to say if that's a problem. I would have to be able to get traces for all of the requests. Basically the first request which returns any given entity determines what properties will be filled (when MergeOption.AppendOnly is used).

    Thanks,


    Vitek Karas [MSFT]
    • Marked as answer by NeilAlderson Thursday, August 26, 2010 10:50 AM
    Wednesday, August 25, 2010 2:33 PM
    Moderator
  • What I was trying to say is that it's not a bug. If you set MergeOption.AppendOnly the materialization will effectively skip entities which already exist on the client. So if your query expands some nav properties of already existing entity, the expanded entities will not be added to the nav property (as that would mean changing the already existing entity, which is not allowed by the MergeOption).

    If some of them do have expanded entities and some of them don't, it's hard to say if that's a problem. I would have to be able to get traces for all of the requests. Basically the first request which returns any given entity determines what properties will be filled (when MergeOption.AppendOnly is used).

    Thanks,


    Vitek Karas [MSFT]

    Hi Vitek,

    Yep I see the problem now. My main query loads Volunteers but before the user can run it I have another query to retrieve all groups and that query expands the Group.Volunteers nav property for each group. So what is happening is that any volunteers belonging to a group are being loaded without the Volunteer.Title nav property and therefore only those not belonging to a group are having the Volunteer.Title nav property added to them since they do not exist yet. I have fixed this by not expanding Groups.Volunteers and lazy loading them later on.

    Regards,

    Neil

    • Marked as answer by NeilAlderson Thursday, August 26, 2010 10:50 AM
    Thursday, August 26, 2010 10:50 AM

All replies

  • Hi,

    I'm not 100% sure this is the reason (as it's unclear how exactly you've got the eventing wired), but a description of what happens during materialization (when we are reading entities from a response of a query):

    We read the incomming response and for every entity we find the already existing entity instance for that entity (if there's one, otherwise we create a new one).
    For each property in the response for that entity we call the property setter on that property.
    The property setter will invoke the PropertyChanged event on that entity (as it should).

    One reason you want this to happen is the UI, basically you want the events to fire so that your data grid updates the UI if the properties have changed. Now it's true that the generated entities don't compare values, so if it happens to have the same value the events still fire.

    You could either edit the entities to compare values and only fire the events when values do change, or handle that in you event handlers.

    Thanks,


    Vitek Karas [MSFT]
    Tuesday, August 24, 2010 12:44 AM
    Moderator
  • Hi,

    I'm not 100% sure this is the reason (as it's unclear how exactly you've got the eventing wired), but a description of what happens during materialization (when we are reading entities from a response of a query):

    We read the incomming response and for every entity we find the already existing entity instance for that entity (if there's one, otherwise we create a new one).
    For each property in the response for that entity we call the property setter on that property.
    The property setter will invoke the PropertyChanged event on that entity (as it should).

    One reason you want this to happen is the UI, basically you want the events to fire so that your data grid updates the UI if the properties have changed. Now it's true that the generated entities don't compare values, so if it happens to have the same value the events still fire.

    You could either edit the entities to compare values and only fire the events when values do change, or handle that in you event handlers.

    Thanks,


    Vitek Karas [MSFT]

    Hi Vitek,

    Thanks for your response. I would say your explanation is spot on. This does indeed seem to be what is occurring. And I can see how it is correct for this behaviour to happen most of the time. The only problem is I can't see how to work around it as you suggest.

    The PropertyChanged event does not include the old and new values in the event args (it would be very useful if it did though) so I can't compare them to see if there is a difference or not. Other than that I don't see how I can make the comparison. It should be possible to modify the generated entities to not fire OnPropertyChanged if there is no difference, as you suggest, but I really don't want to edit those files as they could easily be regenerated as development continues. Is there another way?

    Regards,

    Neil

    Tuesday, August 24, 2010 8:43 AM
  • I've figured it out, but I still have a problem!

     

    So the culprit is my MergeOption:

    dataContext.MergeOption = System.Data.Services.Client.MergeOption.PreserveChanges;
    

    This means that when a query result is received from the data service all the unchanged fields in the client entity are updated (in other words preserve client side changes, update everything else).

    If I use AppendOnly I don't get the PropertyChanged event firing on requery because the context only adds new entities found on the server. Great this sounds like what I need..... BUT my query is using Expand so that it returns some related fields, in this case a Volunteer has a Title entity related to it so my query is like this:

    // This adds a where clause to the query containing all the clauses from the Volunteer Filter
          IQueryable<Volunteer> query = volunteers
            .Expand("Title, Emerg_Relationship")
            .Where(this.ExpressionList.GetCompleteExpression<Volunteer>())
            .OrderBy(v => v.Surname)
            .ThenBy(v => v.Firstname);
    
          // Query is finally executed here
          DataServiceCollection<Volunteer> result = new DataServiceCollection<Volunteer>(query, TrackingMode.None);
          ShowVolunteersEvent.Publish(result);
    

    Looking in Fiddler the data service is correctly expanding the Title and Emerg_Relationship fields but for some reason only a few of the entities in the collection actually have these values, the rest are null? This one has stumped me, is it a bug?

    Regards,

    Neil

    Tuesday, August 24, 2010 3:17 PM
  • The reason is MergeOption as well. With AppendOnly the materialization really doesn't touch the entity if it already exists on the client. This includes all its navigation properties as well.

    Thanks,


    Vitek Karas [MSFT]
    Tuesday, August 24, 2010 4:52 PM
    Moderator
  • The reason is MergeOption as well. With AppendOnly the materialization really doesn't touch the entity if it already exists on the client. This includes all its navigation properties as well.

    Thanks,


    Vitek Karas [MSFT]
    Yes I came to the same conclusion, please see my reply above yours. As stated there is still something wrong, possibly a bug?
    Tuesday, August 24, 2010 9:37 PM
  • What I was trying to say is that it's not a bug. If you set MergeOption.AppendOnly the materialization will effectively skip entities which already exist on the client. So if your query expands some nav properties of already existing entity, the expanded entities will not be added to the nav property (as that would mean changing the already existing entity, which is not allowed by the MergeOption).

    If some of them do have expanded entities and some of them don't, it's hard to say if that's a problem. I would have to be able to get traces for all of the requests. Basically the first request which returns any given entity determines what properties will be filled (when MergeOption.AppendOnly is used).

    Thanks,


    Vitek Karas [MSFT]
    • Marked as answer by NeilAlderson Thursday, August 26, 2010 10:50 AM
    Wednesday, August 25, 2010 2:33 PM
    Moderator
  • What I was trying to say is that it's not a bug. If you set MergeOption.AppendOnly the materialization will effectively skip entities which already exist on the client. So if your query expands some nav properties of already existing entity, the expanded entities will not be added to the nav property (as that would mean changing the already existing entity, which is not allowed by the MergeOption).

    If some of them do have expanded entities and some of them don't, it's hard to say if that's a problem. I would have to be able to get traces for all of the requests. Basically the first request which returns any given entity determines what properties will be filled (when MergeOption.AppendOnly is used).

    Thanks,


    Vitek Karas [MSFT]

    Hi Vitek,

    Yep I see the problem now. My main query loads Volunteers but before the user can run it I have another query to retrieve all groups and that query expands the Group.Volunteers nav property for each group. So what is happening is that any volunteers belonging to a group are being loaded without the Volunteer.Title nav property and therefore only those not belonging to a group are having the Volunteer.Title nav property added to them since they do not exist yet. I have fixed this by not expanding Groups.Volunteers and lazy loading them later on.

    Regards,

    Neil

    • Marked as answer by NeilAlderson Thursday, August 26, 2010 10:50 AM
    Thursday, August 26, 2010 10:50 AM