On the database side, relations between records are stored as foreign keys. On the application side, in addition to fields, entity associations are implemented by the help of navigation properties, which actually are references to other entities. This topic describes the differences between change tracking of "simple" entity properties and navigation properties.
These differences mostly apply to the collection navigation properties. Navigation properties, which refer to a single entity, can be simply stored in the entity copy (which stores the original entity state). Original and current references can be easily compared in the same way as simple entity properties.
Note: |
---|
Actually this can be not so easy: simple entity properties are just usual value types, and entity references are of reference types. Generally, when comparing reference types, equal (by content) objects can have different references. However, LinqConnect caches objects (see Object Identity), thus this is not the case. |
When tracking navigation properties of many sides of associations, the differences became more important. These navigation properties are collections, so it is necessary to track not just the value of such property (reference to a collection), but also the entity references in this collection. So, it is necessary to make a copy of the dependent entity list and compare the original and current lists element-by-element (for each association with a many side of each entity, attached to the context). LinqConnect does this if necessary, however it's obvious that such operations reduce performance. That's why it's better to use our EntitySet type for ollection navigation properties because this type supports notifications on changes in its content.
EntitySets and Change Tracking
Generic class EntitySet<TEntity> has two delegates of the Action<TEntity> type: onAdd and onRemove. These delegates are called when adding or removing an entity from the EntitySet respectively. They can be passed to the public constructor of EntitySet, so when creating a PropertyChanging event, we can notify the context about navigation property change without examining all the collections in all of the attached entities. The notifications are done by the following code, generated by the 'LinqConnect' template (the sample is generated for the Productcategory class and its Products navigation property, irrelevant fragments of code were omitted):
C#csharp | Copy Code |
---|---|
public partial class Productcategory : INotifyPropertyChanging, INotifyPropertyChanged { ... private EntitySet<Product> _Products; ... public Productcategory() { this._Products = new EntitySet<Product>( new Action<Product>(this.attach_Products), new Action<Product>(this.detach_Products) ); ... } ... public EntitySet<Product> Products { get { return this._Products; } set { this._Products.Assign(value); } } ... protected virtual void SendPropertyChanging() { if (this.PropertyChanging != null) this.PropertyChanging(this, emptyChangingEventArgs); } ... private void attach_Products(Product entity) { this.SendPropertyChanging("Products"); ... } private void detach_Products(Product entity) { this.SendPropertyChanging("Products"); ... } } |
Visual Basic | Copy Code |
---|---|
Public Partial Class Productcategory ... Private _Products As EntitySet(Of Product) ... Public Sub New() Me._Products = New EntitySet(Of Product)( _ AddressOf Me.attach_Products, _ AddressOf Me.detach_Products _ ) ... End Sub ... Public Property Products As EntitySet(Of Product) Get Return Me._Products End Get Set Me._Products.Assign(value) End Set End Property Public Event PropertyChanging As PropertyChangingEventHandler _ Implements System.ComponentModel.INotifyPropertyChanging.PropertyChanging Public Event PropertyChanged As PropertyChangedEventHandler _ Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged Protected Overridable Sub SendPropertyChanging() If (Not Me.PropertyChangingEvent Is Nothing) Then RaiseEvent PropertyChanging(Me, emptyChangingEventArgs) End If End Sub ... Private Sub attach_Products(entity As Product) Me.SendPropertyChanging("Products") ... End Sub Private Sub detach_Products(entity As Product) Me.SendPropertyChanging("Products") ... End Sub End Class |
The attach_Products and detach_Products methods are created. These methods are passed to the Products constructor. As it was already mentioned, this means that adding/removing Products collection elements will trigger these methods.
As for tracking, attach_Products/detach_Products methods raises SendPropertyChanging. Either method notifies runtime about which property of which entity is being modified. It allows to limit the number of collectionsto check when submitting changes.
To speed up this process, Entity Set stores the collections of added and deleted entities. So, there is no need to copy or enumerate the list of dependent entities - EntitySet knows what was added or deleted.