I've been converting an application to business and authorization rules defined in a database instead of hard-coded into the business objects.
So, in my AddBusinessRules and AddAuthorizationRule methods, I make a call to the database and load the rules.
Here's the problem I encountered when performance suddenly became hellishly slow in a test case.
One of my business objects had no rules defined for it. Paradoxically, having no rules is slower to process than having rules!
The reason is that if I don't add a business rule to the object, Validation.SharedValidationRules.RulesExistFor() is never set. This means that AddBusinessRules gets called every time an object in a collection is instantiated - instead of only for the first object in the collection!
Here's the constructor for Core.BusinessBase. It's asking "Do shared rules exist?", when it should be asking "Have I already set up the rules I need?". The later question includes the logical condition of "I have no rules."
I sure hope we can get this adjusted, or someone will tell me where my code analysis is flawed. :)
protected BusinessBase()
{
Initialize();
AddInstanceBusinessRules();
if (!Validation.SharedValidationRules.RulesExistFor(this.GetType()))
{
lock (this.GetType())
{
if (!Validation.SharedValidationRules.RulesExistFor(this.GetType()))
AddBusinessRules();
}
}
AddInstanceAuthorizationRules();
if (!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
{
lock (this.GetType())
{
if (!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
AddAuthorizationRules();
}
}
}
David,
I would like to hear Rocky's feedback on this issue.
On another note: is it safe to make a database call from AddBusineesRules?
The framework is still constructing the instance - has it passed all the way through the DataPortal at this point? (I don't recall where it is in the process off the top of my head.)
Joe
JoeFallon1:I would like to hear Rocky's feedback on this issue.
You and me both! :)
JoeFallon1:On another note: is it safe to make a database call from AddBusineesRules?
The framework is still constructing the instance - has it passed all the way through the DataPortal at this point? (I don't recall where it is in the process off the top of my head.)
It works for me. ;) I'm not using mobile objects, so I have no idea whether it matters in that situation.
I've read on the forums that other people have loaded their rules from the database, so I'm guessing it doesn't matter. That, or they aren't using mobile objects either. :)
I do think this is a flaw in the BB ctor code, and unfortunately one that doesn't appear to have a trivial fix, so I'm going to have to think about it a bit. My guess is that the current scheme of lazy-creating the per-type rules list is incorrect, and that the code allowing that to happen should be removed wholesale, but that needs more consideration before doing such major surgery.
However, talking to the database from AddBusinessRules() is also incorrect. There's one simple rule for data access in CSLA:
Only talk to the database from within a DataPortal_XYZ method.
So it is perfectly valid to load business rules from a database table, but you must load them through a read-only object that retrieves them. In other words, AddBusinessRules() can get its rules by retrieving a BusinessRuleList object:
protected override void AddBusinessRule()
{
BusinessRuleList myRules = BusinessRuleList.GetList(this.GetType().Name);
// add rules here
}
This gets you to the same place you are now, but is safe to do within a mobile object scenario.
Thanks Rocky!
I don't access the database directly in the AddBusinessRules method. I call another read-only object that does that in the appropriate way. Sorry, I mis-understood what Joe was asking.
At a first glance, here's what I've been looking at for a quick fix. I'm still testing so it may fall thru.
In ValidationRules, I mimicked the AddRules method where it calls GetTypeRules and passes in true. That's what appears to cause the system to think that rules have been set up. But the only way GetTypeRules is ever invoked with a true parameter value is if I'm adding a rule. So, I added a NoRuleNeeded() method that invokes GetTypeRules with a true parameter.
I'll know if it works in an hour or three. :)
/// <summary>
/// Used to document that there are no rules to add (so the framework will not continue to check for them.)
/// </summary>
public void NoRulesNeeded()
{
GetTypeRules(true);
}
Cool, glad to hear about the way you get the data.
Here’s a preliminary solution that has minimal impact on
the existing codebase
protected BusinessBase()
{
Initialize();
AddInstanceBusinessRules();
if
(!Validation.SharedValidationRules.RulesExistFor(this.GetType()))
{
lock (this.GetType())
{
if
(!Validation.SharedValidationRules.RulesExistFor(this.GetType()))
{
Validation.SharedValidationRules.GetManager(this.GetType(),
true);
AddBusinessRules();
}
}
}
AddInstanceAuthorizationRules();
if
(!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
{
lock (this.GetType())
{
if
(!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
{
Security.SharedAuthorizationRules.GetManager(this.GetType(), true);
AddAuthorizationRules();
}
}
}
}
Rocky
From: david.wendelken
[mailto:cslanet@lhotka.net]
Sent: Friday, December 14, 2007 9:23 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Redundant calls to AddBusinessRules and
AddAuthorizationRules??
Thanks Rocky!
I don't access the database directly in the AddBusinessRules method. I
call another read-only object that does that in the appropriate
way. Sorry, I mis-understood what Joe was asking.
At a first glance, here's what I've been looking at for a quick fix.
I'm still testing so it may fall thru.
In ValidationRules, I mimicked the AddRules method where it calls
GetTypeRules and passes in true. That's what appears to cause the system
to think that rules have been set up. But the only way GetTypeRules is
ever invoked with a true parameter value is if I'm adding a rule. So, I
added a NoRuleNeeded() method that invokes GetTypeRules with a true parameter.
I'll know if it works in an hour or three. :)
/// <summary>
/// Used to document that
there are no rules to add (so the framework will not continue to check for
them.)
/// </summary>
public void NoRulesNeeded()
{
GetTypeRules(true);
}
Excellent! Your fix works like a charm.
It was easy to find the other spots, I just did a search on AddBusinessRules() and AddAuthorizationRules(). :)
A couple of suggestions:
Jim
Copyright (c) Marimer LLC