What is the most elegant way to implement a business rule relating to a child collection in LINQ?
- by AaronSieb
I have two tables in my database:
Wiki
WikiId
...
WikiUser
WikiUserId (PK)
WikiId
UserId
IsOwner
...
These tables have a one (Wiki) to Many (WikiUser) relationship.
How would I implement the following business rule in my LINQ entity classes:
"A Wiki must have exactly one owner?"
I've tried updating the tables as follows:
Wiki
WikiId (PK)
OwnerId (FK to WikiUser)
...
WikiUser
WikiUserId (PK)
WikiId
UserId
...
This enforces the constraint, but if I remove the owner's WikiUser record from the Wiki's WikiUser collection, I recieve an ugly SqlException. This seems like it would be difficult to catch and handle in the UI.
Is there a way to perform this check before the SqlException is generated? A better way to structure my database? A way to catch and translate the SqlException to something more useful?
Edit: I would prefer to keep the validation rules within the LINQ entity classes if possible.
Edit 2: Some more details about my specific situation.
In my application, the user should be able to remove users from the Wiki. They should be able to remove any user, except the user who is currently flagged as the "owner" of the Wiki (a Wiki must have exactly one owner at all times).
In my control logic, I'd like to use something like this:
wiki.WikiUsers.Remove(wikiUser);
mRepository.Save();
And have any broken rules transferred to the UI layer.
What I DON'T want to have to do is this:
if(wikiUser.WikiUserId != wiki.OwnerId) {
wiki.WikiUsers.Remove(wikiUser);
mRepository.Save();
}
else {
//Handle errors.
}
I also don't particularly want to move the code to my repository (because there is nothing to indicate not to use the native Remove functions), so I also DON'T want code like this:
mRepository.RemoveWikiUser(wiki, wikiUser)
mRepository.Save();
This WOULD be acceptable:
try {
wiki.WikiUsers.Remove(wikiUser);
mRepository.Save();
}
catch(ValidationException ve) {
//Display ve.Message
}
But this catches too many errors:
try {
wiki.WikiUsers.Remove(wikiUser);
mRepository.Save();
}
catch(SqlException se) {
//Display se.Message
}
I would also PREFER NOT to explicitly call a business rule check (although it may become necessary):
wiki.WIkiUsers.Remove(wikiUser);
if(wiki.CheckRules()) {
mRepository.Save();
}
else {
//Display broken rules
}