Datagrid entity properties blanking out with combobox selection

Apr 10, 2012 at 6:59 PM
Edited Apr 10, 2012 at 7:12 PM

Hi There,

Im using the self tracking entity generator for WPF and Silverlight and im having a problem with my project and was wondering if anyone could help me out. So I have a datagrid on my main page that is bound to an observable collection. The cells in the grid are bound the a related (Associated) entity property from a lookup table as so:

 

<sdk:DataGrid Visibility="{Binding HasResults, Converter={StaticResource BoolToVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}" x:Name="datagrid" Height="500" HorizontalAlignment="Stretch"  
									  ItemsSource="{Binding Path=IndividualDisabilitiesView}" 
									  SelectedItem="{Binding Path=CurrentIndividualDisability, Mode=TwoWay}" 
									  SelectionMode="Single" IsReadOnly="True" AutoGenerateColumns="False" >
                        <sdk:DataGrid.Columns>
                            <sdk:DataGridTextColumn Header="Case #" Width="Auto" Binding="{Binding Individual.CaseNumberShort}" />
                            <sdk:DataGridTextColumn Header="LName" Width="Auto"  Binding="{Binding Individual.LastName}" />
                            <sdk:DataGridTextColumn Header="FName" Width="Auto"  Binding="{Binding Individual.FirstName}" />
                            <sdk:DataGridTextColumn Header="Disability" Width="Auto" Binding="{Binding Disability.DisabilityDesc}" />
                            <sdk:DataGridTextColumn Header="ADA Code" Width="Auto" Binding="{Binding ADACode.ADACodeDesc}" />
                        </sdk:DataGrid.Columns>
                    </sdk:DataGrid>

 

I select a record from the main grid, I then show a child windows for editing the entity when I press my edit button. I call BeginEdit() on the entity and I have a combobox that is populated with values from my lookup, and bound to the record im editing as shown below. The problem is when i change the value from the combobox, the related (associated) entity property in my datagrid above goes blank until i reload the data from the datasource again after the change is commited. The ID property is changing fine, but the other field goes blank. Why is the related property in my main grid going blank and not updating when I change the value in my combobox??

 

                <ComboBox Margin="0,0,0,0"
                      Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Width="275"
                      ItemsSource="{Binding Path=ADACodes}"
                      DisplayMemberPath="ADACodeDesc" 
                      SelectedValue="{Binding Path=Model.ADACodeID, Mode=TwoWay}"
                      SelectedValuePath="ADACodeID"/>

 

If I use a different binding, and bind the combobox to the navigation property of the entity instead of the ID field using SelectedItem instead of SelectedValue then the cell in my datagrid does not go blank, however Im going to run into problems when I commit the save to the database and I dont think its the proper way to do the binding. As shown below:

 

                <ComboBox Margin="0,0,0,0" Height="25" HorizontalAlignment="Left" VerticalAlignment="Top" Width="275"
                      ItemsSource="{Binding Path=Disabilities}"
                      DisplayMemberPath="DisabilityDesc" 
                      SelectedItem="{Binding Path=Model.Disability, Mode=TwoWay}" />

 

Any ideas would be greatly appreciated. I am using the .Include("") in my service query, and I only seem the be having this issue when I use self tracking entities. 

Thanks!!

Coordinator
Apr 10, 2012 at 7:23 PM

ADACodeID is a primitive property and its related navigation property is ADACode, Am I right on this?

When you set a new value to ADACodeID, the related navigation property ADACode is set back to null, because the old value does not match the new ADACodeID any more. You can verify this by putting a breakpoint at the property setter of ADACode.

Thanks,

Apr 10, 2012 at 8:42 PM

Actually ADACode is actually a string field in the database, its a little misleading. It should probably be named ADACodeDesc instead. I have ADACodeID in both my main table and in my lookup table, and an association between the two using the ADACodeID field.

ADACode is just the text field in the lookup table. Does that make sense? I can attach a picture of my data model if it will make it easier.

Coordinator
Apr 10, 2012 at 9:07 PM

From the code you posted above, if ADACode is a string field, what is "ADACode.ADACodeDesc" then?

Apr 10, 2012 at 9:14 PM
Edited Apr 10, 2012 at 9:17 PM

Im sorry my mistake. Your right in the sample code I posted ADACode is actually the name of the lookup table, Im already using ADACodeDesc for the string field. Whoops! So Your right, the decription you had above is correct.

So how would I go about updating the grids bound text when the value gets changed from the combobox? I think I may be confusing myself here so Ive attached a sample project so I can better explain my situation and show my data model.

In the sample project try to change the ADA Code in the combo box on the right to see the blanking out behavior. Also, the Disability combobox is working correctly but i dont think that is the right binding??

 

I really appreciate you taking the time to help me out with this.

https://skydrive.live.com/redir.aspx?cid=6d95fec17cc0b172&resid=6D95FEC17CC0B172!107&parid=6D95FEC17CC0B172!103&authkey=!AKC91o5dzA0ndMw

Coordinator
Apr 11, 2012 at 2:47 AM

I can compile successfully the sample project you attached, but I cannot run the application because I do not have a sample database.

But, any way, I understand the problem. For the code below:

<ComboBox Height="25" Width="200"
 ItemsSource="{Binding ADACodesSource}"
 DisplayMemberPath="ADACodeDesc"
 SelectedValuePath="ADACodeID"
 SelectedValue="{Binding Path=CurrentIndividualDisability.ADACodeID,
 Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" />

Can you change it to:

<ComboBox Height="25" Width="200"
 ItemsSource="{Binding ADACodesSource}"
 DisplayMemberPath="ADACodeDesc"
 SelectedItem="{Binding Path=CurrentIndividualDisability.ADACode,
 Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" />

Here is the definition of property ADACodeID:

        [DataMember]
        public Nullable<int> ADACodeID
        {
            get { return _aDACodeID; }
            set
            {
                if (_aDACodeID != value)
                {
                    ChangeTracker.RecordOriginalValue("ADACodeID", _aDACodeID);
                    if (!IsDeserializing)
                    {
                        if (ADACode != null && ADACode.ADACodeID != value)
                        {
                            ADACode = null;
                        }
                    }
                    _aDACodeID = value;
                    OnPropertyChanged("ADACodeID");
                }
            }
        }

When you assign a new value to ADACodeID, the related navigation property ADACode is automatically set to null, because the old value of ADACode.ADACodeID does not match to the new ADACodeID any more. But, if you change the navigation property ADACode, ADACodeID will be assigned the new value of ADACode.ADACodeID. Here is the definition of navigation property ADACode:

        [DataMember]
        public ADACode ADACode
        {
            get { return _aDACode; }
            set
            {
                if (!ReferenceEquals(_aDACode, value))
                {
                    var previousValue = _aDACode;
                    _aDACode = value;
                    FixupADACode(previousValue);
                    OnNavigationPropertyChanged("ADACode");
                }
            }
        }

The method FixupADACode(previousValue) re-assigns the new value to ADACodeID.

 

Thanks,

 

 

Apr 11, 2012 at 3:45 AM

Thanks for taking the time to open up the sample project. Your right, if i change the binding to use the SelectedItem as shown above it will work as it should. The only problem is that when i try to save the changes back to the database using that kind of binding, im getting an error about the ID being a duplicate or something similar. I dont have my project in front of me but I will post the exact error message for you when i get back to work tommorow.

Also wanted to say how much i appreciate this template, its been a big time saver for us when developing our business apps. The sample applications have helped my understanding of best practices alot as well. So thanks for that!

I will post the error I was getting tommorow morning. 

Thanks!

Coordinator
Apr 11, 2012 at 3:00 PM

You need to set ChangeTracker.State of all the rows from ADACodesSource to be ObjectState.Unchanged. When a user select a different ADACodeID, the new value for navigation property ADACode should always have a value of ChangeTracker.State = ObjectState.Unchanged. Otherwise, when you save changes, the ADACode will get updated unnecessarily or inserted, which will cause a duplicate row error.

Thanks,

Apr 19, 2012 at 5:11 PM
Edited Apr 19, 2012 at 5:57 PM

Hi,

Sorry it took so long for me to get back to this thread. I was pulled away from the project for a short time. So the error that I am getting when i try to save changes is the following:

"The property 'ADACodeID' is part of the object's key and cannot be changed. Changes to key properties can only be made when the object is not being tracked or is in the Added state."

I have tried to set the ADACodes table to objectstate.unchanged like mentioned above with the following code before I save changes on the context.

Model.ADACode.ChangeTracker.State�=�ObjectState.Unchanged;

But I am still getting the above error when i try to save changes to the context. Now i was doing some experimenting and I can actually get the save to work with the following code before the call to save changes:

Model.Disability.ChangeTracker.State�=�ObjectState.Added;

I don't quite understand why I would need to set it this way. This is also causing a new record to be added to my lookup table which i dont want. 

Now another interesting thing is that in my save call on the service, if i call context.Savechanges() before my call to context.savechanges() the save get completed correctly. However, if i try to save two records with the same lookup selections from my comboboxes, im getting the following error:

"AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges."

Here is my save changes code on my service if this helps:

        public IndividualDisability SaveIndividualDisability(IndividualDisability entity)
        {

                using (var context = new ADATrackingEntities())
                {
                    if (entity.ChangeTracker.State == ObjectState.Added)
                    {
                        // server side validation
                        entity.ValidateObjectGraph();
                        // save changes                      
                        context.IndividualDisabilities.AddObject(entity);                     
                        context.IndividualDisabilities.ApplyChanges(entity);
                        context.AcceptAllChanges();
                        context.SaveChanges();
                        return entity;
                    }
                    else if (entity.ChangeTracker.State == ObjectState.Modified)
                    {
                        // server side validation
                        entity.ValidateObjectGraph();
                        // save changes
                        context.IndividualDisabilities.ApplyChanges(entity);
                        context.AcceptAllChanges();
                        context.SaveChanges();
                        return entity;
                    }
                    else
                    {
                        // Return null or exception
                        return null;
                    }
                }
            
        }

Thanks!

Coordinator
Apr 20, 2012 at 1:45 AM

The error message "The property 'ADACodeID' is part of the object's key and cannot be changed. Changes to key properties can only be made when the object is not being tracked or is in the Added state." is quite self-explanatory.

Since you want to change the value of 'ADACodeID', but it is also part of the primary key, this looks like a database design issue to me.

Thanks,

Apr 23, 2012 at 11:45 PM

I ended up just reloading my data from the server in the callback after i call the savechanges method to update the navigation properties on my datagrid.

 

Thanks again for the help.