I have an Account model with an editable list of users which I can assign to the account.
I need to check for duplicates as I'm adding a user into the list, plus I need to check the database to make sure that the user has NOT been assigned to another account. I'm not trusting the UI to make sure it filters out available users.
BTW this is an MVC application, so any errors will be displayed when the form is posted back. I don't need immediate feedback if I try and add a user already in the list (in memory) or if they've since been allocated to another account.
So, while I can do a list.Contains(userId) call in the list.Add method for the in-memory check, I need to run an expensive rule to look at the database before the save occurs.
Is it just a case of creating a "command style" object like ExistsCommand and use that to create a custom rule in the parent Account model which loops through all the children making sure they are free to be assigned? I think that's the way to go, just need a nod I'm on the right track.
And I'll set the priority of this so it only runs if all the other rules are checked and passed first.
PS.
I don't update the children, users are either added to or removed from the account.
Thanks,
Richard.
Although maybe for the database exist command I just need to the rule on the child object, as I won't need to check previously saved children. But will still need to check for duplicates being added on the parent.
Ok what I did was.....On my parent object was put a check to make sure we do not have any duplicates - ProjectTracker sample helped me out here - Thanks.
protected override void AddBusinessRules()
{
base.AddBusinessRules();
//Check we don't assign the same user twice.
BusinessRules.AddRule(new NoDuplicateUserAccounts { PrimaryProperty = AccountUsersProperty });
}
private class NoDuplicateUserAccounts : Csla.Rules.BusinessRule
{
protected override void Execute(Csla.Rules.RuleContext context)
{
var target = (AccountEdit)context.Target;
foreach (var item in target.AccountUsers)
{
var count = target.AccountUsers.Count(r => r.UserId == item.UserId);
if (count > 1)
{
context.AddErrorResult("Duplicate users on account not allowed");
return;
}
}
}
}
protected override void OnChildChanged(Csla.Core.ChildChangedEventArgs e)
{
//What type of child are we dealing with?
if (e.ChildObject is AccountUserEditList)
{
//Recheck our rules.
BusinessRules.CheckRules(AccountUsersProperty);
OnPropertyChanged(AccountUsersProperty);
}
base.OnChildChanged(e);
}
And then the check to make sure a user was not already assigned was implemented on the child object like this;
protected override void AddBusinessRules()
{
base.AddBusinessRules();
//Check userId can be assigned.
BusinessRules.AddRule(new UserAccountExists { PrimaryProperty = UserIdProperty });
}
class UserAccountExists : BusinessRule
{
protected override void Execute(RuleContext context)
{
var thisUserAccount = (AccountUserEdit)context.Target;
var cmd = new AccountUserExistsCommand(thisUserAccount.UserId);
cmd = DataPortal.Execute(cmd);
if (cmd.AccountUserExists && thisUserAccount.AccountId != cmd.AccountId)
{
context.AddErrorResult("User already assigned to a different Account - Cannot assign them to this account.");
}
//IMPORTANT indicate rule is complete
context.Complete();
}
}
My only question remains, is this the up-to-date way of implementing the business rules 4.5.x style? Guess I could async the AccountUserExistsCommand perhaps - any benefit to doing that here?
Is there a "latest document" that has all the ways of doing business and validation rules? I have the book and pdf guides of course.
Thanks,
Richard.
It should work just fine in the latest version.
Yes, you might want to consider using async command - it may rune significantly faster overall (and in parallell).
And it is only for async rules that you must make sure to call context.Complete().
(the rule engine will call context.Complete() when rule.IsAsync is false)
For my own coding preference I prefer to use InputProperties and have the rule engine retrieve the actual value.
This way - the Execute method does not have to know the type (like AccountUserEdit) and may be more general purpose.
I would probably also change the name of the rule to NotUserAlreadyAssignedOtherAccount or similar.
Copyright (c) Marimer LLC