2. Ensuring Referential Integrity
Since we defined relationships for which
participation was mandatory—that is, each child object must have a
parent object—you may wonder why the child objects were not
automatically deleted when the parent was removed, because removing the
parent object while leaving the child would clearly leave the data set
in an invalid state. In relational database terms, this is a
fundamental requirement to ensure referential integrity.
In SharePoint, applying referential integrity
constraints on a lookup field is optional. Where no constraints are
applied, it is possible to delete parent objects without cascading
deletes to child objects or even raising an error to indicate that such
a delete would invalidate the data structure. At the time of writing,
when using SharePoint Designer 2010 Beta 2 to create lookup columns,
there is no option to configure referential integrity options.
Programmatically Configure Referential Integrity Options
For the moment, we can set referential integrity
options using only the SharePoint user interface or programmatically.
Let’s consider the programmatic option first. Add another button to the
sample application and name it Enforce Integrity Checks. In the on-click event handler, add the following code:
private void button5_Click(object sender, EventArgs e)
{
//disable the button to prevent concurrency problems
button5.Enabled = false;
using (SPSite mySite = new SPSite(SiteUrl.Text))
{
using (SPWeb myWeb = mySite.OpenWeb())
{
//Get a reference to the On-Hire Assets list
SPList assets = myWeb.Lists["On-Hire Assets"];
SPFieldLookup contractRef;
contractRef = assets.Fields["Contract Reference"] as SPFieldLookup;
//there are three options for relationship delete behaviour:
//None - the default, when this option is select referential
// integrity is not enforced
//Cascade - where a parent object is deleted, all child objects
// are automatically deleted
//Restrict - where an attempt is made to delete a parent object
// without first deleting child objects, an
// exception is raised
contractRef.RelationshipDeleteBehavior = SPRelationshipDeleteBehavior.Restrict;
//in order to enforce referential integrity, the lookup
//column must be indexed
contractRef.Indexed = true;
contractRef.Update();
//Get a reference to the Asset Notes list
SPList notes = myWeb.Lists["Asset Notes"];
SPFieldLookup assetRef;
assetRef = notes.Fields["Asset Reference"] as SPFieldLookup;
assetRef.RelationshipDeleteBehavior = SPRelationshipDeleteBehavior.Restrict;
assetRef.Indexed = true;
assetRef.Update();
}
}
//change the text on the button so that we know the job's done
button5.Text += " - Done";
}
This code updates the lookup column properties to
enforce referential integrity by throwing an exception if an attempt is
made to delete a parent object while child objects still exist. The
other option for ensuring referential integrity is to cascade deletes
automatically. In that case, when a parent object is deleted, any child
objects are also automatically deleted.
To see the effects of this change, review the code
for the Delete Sample Data button. Either comment out or delete the
middle loop so that the code reads as follows:
private void button4_Click(object sender, EventArgs e)
{
//disable the button to prevent concurrency problems
button4.Enabled = false;
using(HireSampleDataContext dxWrite = new HireSampleDataContext(SiteUrl.Text))
{
dxWrite.ObjectTrackingEnabled = true;
//loop through the Hire Contracts
foreach (HireContract contract in dxWrite.HireContracts)
{
//for each contract, loop through the assets
foreach (OnHireAsset asset in contract.OnHireAsset)
{
//delete the asset
dxWrite.OnHireAssets.DeleteOnSubmit(asset);
}
//finally delete the contract
dxWrite.HireContracts.DeleteOnSubmit(contract);
}
dxWrite.SubmitChanges();
}
//change the text on the button so that we know the job's done
button4.Text += " - Done";
}
Run the sample application, re-creating the sample
data first if you’ve deleted it. This time, before clicking the Delete
Sample Data button, click the Enforce Integrity Checks button to apply
our constraints. When you click the Delete Sample Data button, an
exception will be thrown, as illustrated. Furthermore, if you stop
execution of the sample application and check your data in the
SharePoint user interface, you’ll find that no changes have been made.
Configure Referential Integrity Options Using the User Interface
You’ve seen how to enforce
constraints on lookup fields programmatically. Let’s now look at how
this can be done using the SharePoint user interface. Navigate to the
Asset Notes list using Internet Explorer. From the ribbon, under List
Tools, select List | List Settings. In the page that appears, find the
Columns section, and then select Asset Reference. You should now see
the Change Column page:
At the bottom of the page is a Relationship section
with option buttons for restricting and cascading delete and a checkbox
to enable or disable referential integrity. The options are currently
set as specified by the code in our sample application; however, so
that our Delete Sample Data function works properly, we need to change
the selected option from Restrict Delete to Cascade Delete. Make the
change and click OK to save it.
Run the sample application. As before,
click the Delete Sample Data button. This time, the function completes
without error, and a quick check of the lists using Internet Explorer
will confirm that all data has been deleted as expected.