Redundant calls to AddBusinessRules and AddAuthorizationRules??

Redundant calls to AddBusinessRules and AddAuthorizationRules??

Old forum URL: forums.lhotka.net/forums/t/4033.aspx


david.wendelken posted on Thursday, December 13, 2007

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();
    }
  }
}

 

JoeFallon1 replied on Thursday, December 13, 2007

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

 

ajj3085 replied on Friday, December 14, 2007

I believe it would be safe, however I would imagine that you can't mark you DP_C as RunLocal though.  Your on the "server" when the constructor for the BO runs.

david.wendelken replied on Friday, December 14, 2007

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. :)

 

RockfordLhotka replied on Friday, December 14, 2007

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.

david.wendelken replied on Friday, December 14, 2007

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);
}

 

RockfordLhotka replied on Friday, December 14, 2007

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);
}

 



RockfordLhotka replied on Friday, December 14, 2007

I put the above fix into 3.0.4 and 3.5, because I was already messing with similar things due to a race condition elsewhere - both fixes were complimentary. All updates are in svn now (and this particular update has changes in several locations beyond the ctor btw - and also in ROB).

david.wendelken replied on Friday, December 14, 2007

Excellent!  Your fix works like a charm. 

It was easy to find the other spots, I just did a search on AddBusinessRules() and AddAuthorizationRules(). :)

jimmunn replied on Friday, February 15, 2008

A couple of suggestions:

Jim

Copyright (c) Marimer LLC