Identifying ADO.NET Constraint Exceptions
ADO.NET constraint violations can occur as a result of a number of reasons including invalid foreign key references between a parent and child table (meaning orphans exist in the child table) and null violations when a value isn't provided for a column that prohibits null values.
For me, the latter happens from time to time when a field in a strongly-typed dataset has been named incorrectly and will never be populated or the field is not returned by the fetch statement that fills that table. Often times, it can be a bit of a pain to determine which record or records caused the ConstraintException. I use the following dataset extension method to retrieve a friendly list of those violations. Typically my strongly typed dataset will raise an event from the catch block to allow the list of messages to be served to the consumer. At a minimum, a breakpoint in the catch makes reviewing and rectifying the situation relatively painless.
/// <summary>
/// Extracts a list of row error messages from a datatable.
/// </summary>
/// <param name="dt">Reference to the DataTable being interrogated</param>
/// <returns>List of error messages</returns>
public static List<string> GetConstraintExceptionMessages(this DataTable dt)
{
List<string> rowErrorMessages = new List<string>();
rowErrorMessages.AddRange(dt.Rows.Cast<DataRow>()
.Where(r => r.HasErrors)
.Select(r => string.Format("{0} [{1}]", r.RowError, r.GetType().ToString())));
return rowErrorMessages;
}
/// <summary>
/// Extracts a list of row error messages from a dataset.
/// </summary>
/// <param name="ds">Reference to the DataSet being interrogated</param>
/// <returns>List of error messages</returns>
public static List<string> GetConstraintExceptionMessages(this DataSet ds)
{
List<string> rowErrorMessages = new List<string>();
foreach (DataTable dt in ds.Tables)
rowErrorMessages.AddRange(dt.GetConstraintExceptionMessages());
return rowErrorMessages;
}
Usage:
DataSet ds = new DataSet() ;
try
{
ds.EnforceConstraints = false;
//Fill the dataset and perform any additional operations
ds.EnforceConstraints = true;
}
catch (System.Data.ConstraintException)
{
List<string> errorMessages = ds.GetConstraintExceptionMessages();
throw;
}
catch (Exception)
{
throw; //non constraint exception
}
I've been a big fan of extension methods (over static helper classes) since they were introduced because there's no need to try to remember which helper contains the needed functionality. Instead, the extension method acts as a form of encapulation through its association to the type for which it was written.
~D