Object States and Change-Tracking
LINQ to SQL objects always participate in some state. For example, when LINQ to SQL creates a new object, the object is in Unchanged state. A new object that you yourself create is unknown to the DataContext and is in Untracked state. Following successful execution of SubmitChanges, all objects known to LINQ to SQL are in Unchanged state. (The single exception is represented by those that have been successfully deleted from the database, which are in Deleted state and unusable in that DataContext instance.)
The following table lists the possible states for LINQ to SQL objects.
An object not tracked by LINQ to SQL. Examples include the following:
An object retrieved by using the current DataContext and not known to have been modified since it was created.
An object which is attached to a DataContext. For more information, see Data Retrieval and CUD Operations in N-Tier Applications (LINQ to SQL).
An object known to have been modified since it was retrieved. This causes a database UPDATE during SubmitChanges.
An object marked for deletion, causing a database DELETE during SubmitChanges.
An object that has been deleted in the database. This state is final and does not allow for additional transitions.
You can explicitly request Inserts by using InsertOnSubmit. Alternatively, LINQ to SQL can infer Inserts by finding objects connected to one of the known objects that must be updated. For example, if you add an Untracked object to an EntitySet<TEntity> or set an EntityRef<TEntity> to an Untracked object, you make the Untracked object reachable by way of tracked objects in the graph. While processing SubmitChanges, LINQ to SQL traverses the tracked objects and discovers any reachable persistent objects that are not tracked. Such objects are candidates for insertion into the database.
For classes in an inheritance hierarchy, InsertOnSubmit(o) also sets the value of the member designated as the discriminator to match the type of the object o. In the case of a type matching the default discriminator value, this action causes the discriminator value to be overwritten with the default value. For more information, see Inheritance Support.
You mark a tracked object o for deletion by calling DeleteOnSubmit(o) on the appropriate Table<TEntity>. LINQ to SQL considers the removal of an object from an EntitySet<TEntity> as an update operation, and the corresponding foreign key value is set to null. The target of the operation (o) is not deleted from its table. For example, cust.Orders.DeleteOnSubmit(ord) indicates an update where the relationship between cust and ord is severed by setting the foreign key ord.CustomerID to null. It does not cause the deletion of the row corresponding to ord.
LINQ to SQL performs the following processing when an object is deleted (DeleteOnSubmit) from its table:
When SubmitChanges is called, a DELETE operation is performed for that object.
The removal is not propagated to related objects regardless of whether they are loaded. Specifically, related objects are not loaded for updating the relationship property.
After successful execution of SubmitChanges, the objects are set to the Deleted state. As a result, you cannot use the object or its id in that DataContext. The internal cache maintained by a DataContext instance does not eliminate objects that are retrieved or added as new, even after the objects have been deleted in the database.
You can call DeleteOnSubmit only on an object tracked by the DataContext. For an Untracked object, you must call Attach before you call DeleteOnSubmit. Calling DeleteOnSubmit on an Untracked object throws an exception.
Removing an object from a table tells LINQ to SQL to generate a corresponding SQL DELETE command at the time of SubmitChanges. This action does not remove the object from the cache or propagate the deletion to related objects.
To reclaim the id of a deleted object, use a new DataContext instance. For cleanup of related objects, you can use the cascade delete feature of the database, or else manually delete the related objects.
The related objects do not have to be deleted in any special order (unlike in the database).
You can detect Updates by observing notifications of changes. Notifications are provided through the PropertyChanging event in property setters. When LINQ to SQL is notified of the first change to an object, it creates a copy of the object and considers the object a candidate for generating an Update statement.
For objects that do not implement INotifyPropertyChanging, LINQ to SQL maintains a copy of the values that objects had when they were first materialized. When you call SubmitChanges, LINQ to SQL compares the current and original values to decide whether the object has been changed.
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<TEntity> and EntityRef<TEntity>) guarantee that the bidirectional references are consistent for one-to-many and one-to-one relationships. If the object model does not use EntitySet<TEntity> or EntityRef<TEntity>, 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.