Generally, to know which entities were modified, one should store copies of all the entities. When an entity is attached to a DataContext (is read from the database or inserted to it) - a copy should be created. User does not have a reference to this copy, unlike the original entity, so this copy cannot be modified and is always in an unchanged state. When performing submit, each copy is compared with the current entity, and it is determined whether the entity must be updated in the database, and which entity properties must be updated. After database update, the copy is updated to the current state and is used in the next submit operation if such is performed.
If an entity has no key, it cannot be cached and change tracking is not enabled for such entity. LinqConnect does not allow modifications of such entities. An exception is raised when such entity is created or deleted, and updates of such entity are ignored. |
However, creating and storing a copy for each retrieved entity is costly, and in addition you need to compare all entities with their copy when performing submit. Performance overhead can be even worse when we retrieved the whole table and updated only few entities.
That's why it is better to store only copies of modified entities. For example use special events that occur when an entity is changed. LinqConnect uses the INotifyPropertyChanging interface from the System.ComponentModel namespace. This member has the only member - the PropertyChanging event, which should be raised right before the tracked property is modified. The subscribed handler of this event, generated by LinqConnect can process it and create an entity copy.
For example, all entities, generated by the LinqConnect template, implement this interface and each mapped property setter launches the PropertyChanging event just before modifying the corresponding private field:
C#csharp | Copy Code |
---|---|
public partial class Product : INotifyPropertyChanging, INotifyPropertyChanged { private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(System.String.Empty); ... private string _ProductName; [Column(Storage = "_ProductName", CanBeNull = false, DbType = "VARCHAR(50) NOT NULL")] public string ProductName { get { ... } set { if (this._ProductName != value) { this.OnProductNameChanging(value); this.SendPropertyChanging(); this._ProductName = value; ... } } } ... public event PropertyChangingEventHandler PropertyChanging; ... protected virtual void SendPropertyChanging() { if (this.PropertyChanging != null) this.PropertyChanging(this, emptyChangingEventArgs); } ... } |
Visual Basic | Copy Code |
---|---|
Public Partial Class Product Implements INotifyPropertyChanging, INotifyPropertyChanged Private Shared emptyChangingEventArgs As PropertyChangingEventArgs = _ New PropertyChangingEventArgs(System.String.Empty) ... Private _ProductName As String ... <Column(Storage := "_ProductName", CanBeNull := false, DbType := "VARCHAR(50) NOT NULL")> _ Public Property ProductName As String Get ... End Get Set If (Object.Equals(Me._ProductName, value) = false) Then Me.OnProductNameChanging(value) Me.SendPropertyChanging() Me._ProductName = value ... End If End Set End Property ... Public Event PropertyChanging As PropertyChangingEventHandler _ Implements System.ComponentModel.INotifyPropertyChanging.PropertyChanging ... Protected Overridable Sub SendPropertyChanging() If (Not Me.PropertyChangingEvent Is Nothing) Then RaiseEvent PropertyChanging(Me, emptyChangingEventArgs) End If End Sub ... End Class |
And now let's illustrate using this interface by the following example:
C#csharp | Copy Code |
---|---|
Product shakespeareWorks = context.Products.Where(p => p.ProductID == 7007).Single(); shakespeareWorks.ProductName = "Shakespeare W. Shakespeare's dramatic works"; |
Visual Basic | Copy Code |
---|---|
Dim shakespeareWorks As Product = context.Products.Where(Function(p) p.ProductID = 7007).[Single]() shakespeareWorks.ProductName = "Shakespeare W. Shakespeare's dramatic works" |
When we retrieve the product, the context starts tracking it. However the context knows that the Product class implements INotifyPropertyChanging, so it subscribes to the PropertyChanging event and does not create a copy at this moment.
Only when we change ProductName (or any other property), SendPropertyChanging is called and LinqConnect runtime receives the PropertyChanging event, creates a copy and marks 'shakespeareWorks' to be checked for changes when submitting modified entities.
Note: |
---|
Besides INotifyPropertyChanging, LinqConnect entities implement the INotifyPropertyChanged interface. This interface is not used by LinqConnect runtime. It is implemented to ease binding. |