Help with Instance Validation Rules (Deadline Approaching)

Help with Instance Validation Rules (Deadline Approaching)

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


wjcomeaux posted on Thursday, March 12, 2009

We have an object that calls AddInstanceBusinessRules to do
ValidationRules.AddInstanceRule<Merchant_ER>(IsSENumberValid, "SENumber")

The IsSENumberValid simply returns true or false if the object is valid.

For some reason the same object is always used. If my app starts and I load object instance A and then load object instance B and change the SENumber of B my IsSENumberValid is called twice (one for each object) but the target is always A.

I've tried every permutation I can think of.
AddInstanceBusinessRules calling AddInstanceRule
AddCustomRules calling AddInstanceRule or AddRule
and the above when IsSENumberValid is static and not static. Some options didn't work at all.

So, what would be the appropriate way to set this up? I'm using the latest CSLA build.

TIA,
Will

P.S. This one is serious for me as my managers want to release this code on Sunday........

 

rsbaker0 replied on Thursday, March 12, 2009

This seems like an unusual use, as usually if IsValid is false the specific rule causing the object to be broken is associated with a specific property and associated rule.

Another issue you will need to be concerned with (assuming you meant IsValid when you said "object is valid") is that this will depend on the order your rules execute in. If this rule executes before the rule that breaks the object, then it will report success even though the object ultimately will be broken.

That being said, have you tried this as class rule as opposed to an instance rule?

wjcomeaux replied on Thursday, March 12, 2009

Actually I have. That was my initial starting point

AddCustomRules calling ValidationRules.AddRule(IsDiscoverSENumberValid, "DiscoverSENumber");

IsDiscoverSENumberValid was static to accomodate this. However, this means the rule is essentially global to all instances of my object.

I just ran my BOs through an NUNIT test case

Clients.Client c = new Clients.Client(633118);
string dap = c.Merchant.DiscoverAcquiringProgram.AcquiringProgramName;
string se = c.Merchant.DiscoverAcquiringProgram.DiscoverSENumber = "555";
c.Merchant.DiscoverAcquiringProgram.AcquiringProgramName =
"IMAP";
c.Merchant.DiscoverAcquiringProgram.DiscoverSENumber =
"1";

and what I found was the object worked perfectly. Setting DiscoverSENumber to 555 did so on the MAP acquiring program object. After that I set the object to IMAP. This is a state manager so the MAP object essentially goes away and a new IMAP object is created and returned. Then setting DiscoverSENumber = 1 did so and validated ONLY on the IMAP object.

What I'm finding is that my BL works as expected until I start using it in the UI where databinding comes into play.
I think something is not correctly releasing a binding which is forcing the MAP object to remain in memory.

What a PITA...

Thanks,
Will

rsbaker0 replied on Friday, March 13, 2009

I can't see why your rule (static or otherwise) would be related to your MAP memory propblem without seeing the code.

However, I wouldn't necessarily be concerned about static rules being global to all your object instances. Our application has hundreds of validation rules, and so far all of them are static. The static rule still has full access to the specific instance data -- the instance is passed as the first parameter to the rule -- so you can effectively do the same processing in either rule type IMHO. Whatever test you did to decide whether to add an instance rule in the first place can be used in a static rule to decide whether to bypass the rule processing entirely.  With instance rules, I believe you incur additional overhead as the rules have to be loaded each time an object is instantiated, but that's just my $0.02.

RockfordLhotka replied on Friday, March 13, 2009

I honestly don't think there is any good reason to use per-instance rules anymore, as I think you can accomodate all scenarios with per-type rules. Certainly nothing you are describing indicates the need for per-instance rules.

Per-instance rules incur substantial overhead (performance and memory) compared to per-type rules.

While a static rule method is shared across all instance of your type, that's a non-issue. The rule should act on the properties and fields of the specific object passed in via the target parameter, so it always operates on the correct object instance.

wjcomeaux replied on Friday, March 13, 2009

OK below is the specific code I am using for this business object. And as you guys mentioned this works perfectly. If I instantiate a MAP object the validation rule loads once. If I change SE on MAP the proper instance is validated. Then I change from MAP to IMAP via my state manager and the validation does not get loaded again, which is correct. Finally, setting SE number on the IMAP object does indeed ONLY validate it on the IMAP object. So, using NUNIT straight against by business layer works absolutely flawlessly.

The issue starts in the windows UI and I'm sure it has to do with the POS databinding code I'm forced to use. Not sure you guys can help with this but maybe someone has seen it before. In the UI the problem is as I described above. When I change from MAP to IMAP and change the se on the IMAP object the validator fires for both the MAP AND IMAP objects but with the value that is intended for only the IMAP object. I've even gone in and made sure that I am unbinding everything correctly before switching to IMAP and then I rebind.

If anyone has experienced this and can shed some light it's much appreciated. However, for all intensive purposes the CSLA objects are working 100% so this really is a non CLSA issue at this point.

Thanks for everything -- Will

Here is my current code, just fyi.

protected
override void AddCustomRules()
{
ValidationRules.AddRule<DiscoverMerchant_ER>(IsDiscoverSENumberValid, "DiscoverSENumber");
}

private static bool IsDiscoverSENumberValid(DiscoverMerchant_ER target, Csla.Validation.RuleArgs e)
{
   if (target.AcquiringProgram.ToUpper() == "IMAP" || target.AcquiringProgram.ToUpper() == "MAP")
   {
      if (target.DiscoverSENumber.Length > 5)
      {
         if (!Hps.Common.Utilities.Validation.IsMod10(target.DiscoverSENumber.Substring(4)))
         {
            e.Description = "Discover SE Number is not a valid Mod 10 number.";
            return false;
         }
      }
      
else
      
{
         e.Description = "Discover SE Number is not a valid Mod 10 number.";
         return false;
      }
   }
   return true;
}

RockfordLhotka replied on Friday, March 13, 2009

wjcomeaux:

When I change from MAP to IMAP and change the se on the IMAP object the validator fires for both the MAP AND IMAP objects but with the value that is intended for only the IMAP object. I've even gone in and made sure that I am unbinding everything correctly before switching to IMAP and then I rebind.

So you are saying that changing one textbox (or whatever) causes two objects' properties to be set?

That certainly seems like a bug in the UI and/or data binding code.

wjcomeaux replied on Friday, March 13, 2009

Yes, that is exactly correct.

Problem is we aren't using standard windows databinding. We have this library that handles all bindings and error providers for us that, until now, knew nothing about CSLA so it didn't know how to listen to CSLA validation rules. Now it does, sorta...

Fintanv replied on Friday, March 13, 2009

Well the one situation where I add instance rules is where the rule metadata is stored in the database, and I load the rules based on some property value(s) of the business object.  This means each child in a collection may have different rules set, usually surrounding user configured values.  In a specific example I allow my users to set up product flows.  These flows allow the users to set the data they wish to collect, number of samples, datatype etc.  They also allow the users to set up rules surrounding their samples (allowed ranges for example), and the rules vary by the data type they select.  These configured flows are then used to create data entry screens where the rules are applied.

Copyright (c) Marimer LLC