Reputation: 17491
Two days ago, I have asked this question on how to modify deletion rules of foreign key constraints at runtime. That question has been answered, but now I would like to generalise my program:
Currently I have:
try
{
dt_MainTable1.Rows[0].Delete();
}
catch (Exception ex)
{
if (MessageBox.Show("In order to avoid data corruption, not only " +
" the required tupple will be deleted, " +
"but also the ones in other tables, " +
"referring to that tupple. " +
"Is that what you want?",
"Warning",
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
(ForeignKeyConstraint)(dt_SubTable1.Constraints[0]).DeleteRule
= Rule.Cascade;
dt_MainTable1.Rows[0].Delete();
(ForeignKeyConstraint)(dt_SubTable1.Constraints[0]).DeleteRule
= Rule.None;
}
}
... and this is what I would like to have: I try to do something (like a modification or a deletion), but I fall into a System.Data.InvalidConstraintException
, based on a Constraint
. Then I ask a question and based on the answer I turn the impacted Constraint
's Deletion Rule
or Modification Rule
into None
or Cascade
. The big problem: can I retrieve the impacted Constraint
from the generated System.Data.InvalidConstraintException
? (I've been debugging and checking the properties of the System.Data.InvalidConstraintException
, but I didn't find anything), just to show what I want (see the ex.FindRelevantConstraints()
method):
try
{
LaunchDeleteAction(...);
}
catch (Exception ex)
{
if (MessageBox.Show("In order to avoid data corruption, not only " +
" the required tupple will be deleted, " +
"but also the ones in other tables, " +
"referring to that tupple. " +
"Is that what you want?",
"Warning",
MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
for each (ForeignKeyConstraint C in ex.FindRelevantConstraints() <===
C.DeleteRule = Rule.Cascade;
LaunchDeleteAction(...);
for each (ForeignKeyConstraint C in ex.FindRelevantConstraints() <===
C.DeleteRule = Rule.None;
}
}
Edit after some more investigation
Meanwhile I've found out that the Exception
, ex
, contains the name of the corresponding Constraint
, as you can see in following watch window excerpt:
ex.Message : Cannot delete this row because constraints are enforced on relation Constraint1, and deleting this row will strand child rows.
I mean, it can't possibly be the intention to parse the Exception
's message in order to get a reference to the Constraint
object I'm looking for?
Desperate edit
In the meantime, I've decided to give special names to the Constraint
objects I'm creating, like you can see here:
dt_SubTable1.Constraints.Add(
new ForeignKeyConstraint("|FKC|MainTable.SubId|SubTable.Id|",
dt_SubTable.Columns["Id"],
dt_MainTable.Columns["SubId"])
{ DeleteRule = Rule.None,
UpdateRule = Rule.None });
As you can see, I can use the pipe character for separating the table names and the dots for finding the column names, but that's really a desperate idea :-)
Upvotes: 3
Views: 721
Reputation: 172280
To directly answer the question in your title:
Can I retrieve the impacted constraint(s) from a InvalidConstraintException?
No, unfortunately, you can't.
According to the reference source, InvalidConstraintException
does not store any useful information apart from the message text and (if applicable) an inner exception.
Some additional reading reveals that the System.Data
methods pass all relevant information (e.g. the constraint name) to the ExceptionBuilder
methods, but, alas, this information is only used to create the Exception message and then thrown away.
Upvotes: 3
Reputation: 7454
As a general rule it is better to test for the presence or absence of something that will cause an exception rather than to catch it.
Likewise handling user interaction in a catch block is not ideal and changing the data integrity constraints to perform the operation seems the wrong way to deal with it.
A better approach might be to:
If the relationship is a simple as the main and sub tables in the example you could try something like
if (dt_SubTable1.AsEnumerable.Any(r => s => r.Field("ForeignIdField") == MainId))
If you have a few DataTable
instances you could iterate over them checking the Constraints
properties in code.
foreach (var constraint in dt_Sub1.Contstraints)
{
if (constraint is ForeignKeyConstraint && constraint.RelatedTable.TableName = dt_Main.TableName)
// check for matching data
}
If you are trying a more generalized approach This question has several methods showing how you can find the foreign keys for a given table. You could create a method FindRelevantConstraints(string tableName)
wrapping the system stored procedure sp_fkeys
which would return both the table name and the name of the field.
This will allow you to create a query for any and all sub tables that depend on your primary table using the id value
Upvotes: 3