I get this error if I follow these steps:
- Change property Y of an object (an associated entity property)
- Attempt to submit changes
- At this point the value of Y and the value of X (the underlying key) are not in agreement -- LINQ to SQL apparently doesn't synchronize these until GetChangeSet is called.
- An expected error occurs due to some business logic or database level constraint during the update operation.
- At this point the value of Y is in agreement with X because GetChangeSet was called.
- Change the value of Y to Nothing (aka null).
- Call GetChangeSet.
The error occurs on the last step because the value of X and the original value of X (returned by GetOriginalEntityState) are different, and the new value does not agree with Y? Is that why? Is this a bug in LINQ to SQL. Must be because I don't see the same behavior if I change Y to another (non-null) value instead during step 5. What's the right way around this? I can see a few ways:
- Discard the DataContext when an error occurs and leave the UI as-is. I don't like this because then optimistic cocurrency change conflicts cannot be detected. The new context doesn't have the original values in it that were populated at the same time the UI was populated, so if the UI has any s开发者_如何学Pythontale values in it, they will cause data in the database to revert.
- Refresh the datacontext (OverwriteCurrent) and leave the UI as-is. I don't like this for the same reason as #1.
- Refresh the datacontext (OverwriteCurrent) and re-populate the UI. I don't like this because then the error message just presented to the user does not show the user the error they made and allow them to correct it. It also discards all the other changes the user may have made.
- When the error occurs, explicitly retrieve the key for Y that corresponds to the original value of X and reset Y, then call GetChangeSet to re-synchronize X (X is read-only or private so I can't reset it directly). This seems to work, but seems like a hack, and may require lots of code for other similar errors.
Is there a better solution. Is this something that should be reported?
Per MSDN:
For updates to relationships, the reference from the child to the parent (that is, the reference corresponding to the foreign key) is considered the authority. The reference in the reverse direction (that is, from parent to child) is optional. Relationship classes (EntitySet and EntityRef) guarantee that the bidirectional references are consistent for one-to-many and one-to-one relationships. If the object model does not use EntitySet or EntityRef, and if the reverse reference is present, it is your responsibility to keep it consistent with the forward reference when the relationship is updated.
If you update both the required reference and the corresponding foreign key, you must make sure that they agree. An InvalidOperationException exception is thrown if the two are not synchronized at the time that you call SubmitChanges. Although foreign key value changes are sufficient for affecting an update of the underlying row, you should change the reference to maintain connectivity of the object graph and bidirectional consistency of relationships.
http://msdn.microsoft.com/en-us/library/Bb386982(v=VS.90).aspx
It looks like this bug has been fixed in the latest .NET and/or Visual Studio 2010. The code generated for the DBML file now contains code to update the underlying foreign key value even when setting the association property to null:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Customer_Order", Storage="_Customer", ThisKey="fkCustomer", OtherKey="id", IsForeignKey=true)]
public Customer Customer
{
get
{
return this._Customer.Entity;
}
set
{
Customer previousValue = this._Customer.Entity;
if (((previousValue != value)
|| (this._Customer.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Customer.Entity = null;
previousValue.Orders.Remove(this);
}
this._Customer.Entity = value;
if ((value != null))
{
value.Orders.Add(this);
this._fkCustomer = value.id;
}
else
{
this._fkCustomer = default(Nullable<int>);
}
this.SendPropertyChanged("Customer");
}
}
}
I think the this._fkCustomer = default(Nullable<int>);
must not have been there in the code I was testing when I originally posted this question. So either I had my DBML set up wrong, or this problem was fixed.
I recently experience this issue using linq to sql, my classes where generated with sqlmetal, and displayed using WPF bindings.
When displaying a combox of values from a table I had a foreign key reference with, I was setting the SelectedValuePath to the ID. However when the Id was updated on my record the associated member was not updated to match the Id value.
(Address.provID would be 1 and Address.Province would be null)
I updated the binding to update the SelectedItem and not the SelectedValuePath.
<ComboBox Name="provList" DisplayMemberPath="Code" ItemsSource="{Binding Source={x:Static list:GlobalList.ProvinceList}}" SelectedItem="{Binding Path=Provinces}" Width="75" Height="23" HorizontalAlignment="Left" Margin="5,0,0,0" VerticalAlignment="Top"/>
精彩评论