Validation rules depending on taken action

Validation rules depending on taken action

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


PabloDiaz posted on Thursday, August 07, 2008

Hi All,

This is the scenario this time.

I have an object which can be saved and can be approved and... etc.

The validation rules for saving are diferent than the validation rules for approving, etc.

How CSLA manage this.

All ideas are welcome,

Thanks in advance

rsbaker0 replied on Thursday, August 07, 2008

This can be done (although some purists here would suggest you create two different objects, one for creating and one for approving).

With a single object, you 'd just need some way to know the state of the object (e.g. whether it has been approved or not). You can make your approval based validation rules (and dependant property rules) file when the object is set to "approved".

Rules that don't apply when the state isn't approved can simply return true.

(There are several variations on this general approach, but this is the idea -- if you only want a rule to apply in a certain situation, it just needs something to test for so it can bypass the inapplicable logic)

PabloDiaz replied on Thursday, August 07, 2008

Hi,

If I keep creating objects for every single stuff I'll finish up with 70 objects.

For example, my app has to deal with status: 7 in total. (Validation change with status)

This mean that I am going to create a parent object and then create 7 objects for every status based on the parent. That could be acceptable for me. But now you tell me that purists recommends to create an object for saving and another for approving...

Then If I'll finish with a long set of object. For example:

                                                                      (Parent)

            (Status1)                                                        (Status2)                                      Status3

(Status1Save,Status1Approve)      (Status2Save,Status2Approve,Status2Reject)        (....................)

My object tree will look like a real Big Tree. I don't see a clear way to manage the transition between objects. Because in my case you just show the object to the user on its correspondent view and then the user can perform actions on it. And that is the requirement.

May I ask you to explain me more about your suggestion, I have not grasped.

Your help is much appreciated.

rsbaker0 replied on Thursday, August 07, 2008

Well, I didn't see your status as being an object but rather a property. And when I said make a separate object, I just meant that there is a strong following here of the idea that you construct your object to match your use case, and don't try to share objects between different use cases if there is different behavior.

(So, if you have a user interface for entering new things that later need to be approved, build an object that matches that. Your approval screen might look very different, so build an object that matches that use case.)

But let's forget that for now and look at the single object.

If you have a previously unapproved object and now you are trying to approve it, presumably you'll be changing the status from "Unapproved" to "Approved" (there may be a chain of statuses but this should illustrate).

Validation rules fire only if you call CheckRules or change a property, and the specific rules that fire are attached to the properties that are changed (or have rules, in the case when you call CheckRules) 

So, if you have rules that apply only prior to approval, then the first thing they do is check whether the status is approved. If so, they don't apply, so you just return true. Similarly for rules that apply only when approving. They just return true unless you're approving, otherwise they do their approval specific stuff.

You don't have to use your status, you could even just have a separate state for your processes that you set that says "Approving" versus "Just Editing" (or creating, although you can tell when an object is new from the framework). You can even make specific validation rules dependent on that property, so if you activate the approval process, all of the rules you have linked to it would fire, etc.

In your case, you have 7 statuses, so I'd assume you would need to know what the previous status was also, and that you have rules that only care if "Previous status is 4 and new status is 6" or something like that.

It's easy to make a mess and create a maintenance problem when you have rules dependent on each other or have similar coupling or dependencies, which is where the "just make two objects, one for creating and one for approving" recommendation comes from.

 

PabloDiaz replied on Thursday, August 07, 2008

Sounds really good.

I want to clarify something because I didn't explain it clear on my previous post. Status is actually a property of my object, what I wanted to show with the so called object tree diagram is this: I have an object called 'Project', this is a parent object. Because I have seven status for this object and validation rules are different for every status. Then to avoid the complexity of spanning the same object across all the use cases I preferred to create seven object which inherit my 'Project' object. But I also has  to deal with an extra complexity, it is that you can perform more actions than just save on every child object, you also can approve or reject, and the set of required fields (validation rules) for every action is diferent, a set of few properties are required to save, a set of some others properties are required to approve. The view will always have the same structure (look) except that some buttons will be enabled, some not. Some textbox will be readonly, some not. All this depending on role. (I want to point that if I separate actions, e.g. one object for Save and one object for Save then I'll finish with this: Project (parent), ProjectChild1(to handle status1) ProjectChild2(to handle status2)...  then ProjectGrandChild1(to handle ProjectChild1 Save), ProjectGrandChild2(to handle ProjectChild1 Approve), ProjectGrandChild3(to handle ProjectChild1 Reject) and the list goes on...

In this moment I am starting to think that spanning the same object across all the use cases is a more viable solution, because maybe creating objects for every use case can reduce complexity in one site is adding complexity in another site. Just imaging how painful will be to handle the transitions from state to state, actionobject to action object. Because it's not about openning an object to save it or openning an object to approve it. It's about opening an object on which you perform the actions that you prefer,  name it SAVE or APPROVE.

I also have to deal with the fact that validation rules (which fields are required for certain status, etc.) must come from a dynamic data repository giving the final user the hability to change the rule when they please.

Right now, we have an app which does all this, with only one object being spanned accross all the use cases and calling the validation and authorization data from a sql database. It works good. The problem is for new comers is almost impossible to understand all the plumbing. And I wanted to reengineer this app with a more understandable approach.

But I'm getting to the conclusion that this dynamism can not be accomplished using CSLA. Am I wrong? If somebody tell me that is possible then I'll start to dig more onto CSLA and make it work, so let me know your opinion about this case, please.

Thanks for reading, hope to read your comments.

ajj3085 replied on Thursday, August 07, 2008

You can do this, because validation rules may take other state of the object into consideration.

For example, if you have a property that must be entered before the Approved object can be saved, you can have a SetApproved method, which will change the status and then can call ValidationRules.CheckRules.  The rules can choose to always return true until the status is Approved, in which case the rule can continue checking.. something like this:

private static bool EnsureApproveReasonProvider( MyObj target, RuleArgs e ) {
    bool result;

    if ( target.status == "Approved" ) {
        result = CommonRules.StringRequired( target, e );
    }
    else {
        result = true;
    }

    return result;
}

rsbaker0 replied on Thursday, August 07, 2008

PabloDiaz:
...

Right now, we have an app which does all this, with only one object being spanned accross all the use cases and calling the validation and authorization data from a sql database. It works good. The problem is for new comers is almost impossible to understand all the plumbing. And I wanted to reengineer this app with a more understandable approach.

But I'm getting to the conclusion that this dynamism can not be accomplished using CSLA. Am I wrong? If somebody tell me that is possible then I'll start to dig more onto CSLA and make it work, so let me know your opinion about this case, please....

I have this exact same situation (in multiple BOs) in an application I am porting to CSLA , and really didn't see how I could port the code to CSLA to at first. A year later, I'm quite confident it can be done, and you can refactor pieces out into validation rules as you gain knowledge.

Our legacy code has UI (message boxes, other prompts, etc) mixed up in the business logic. One of the main reasons we went with CSLA was that it gives you the proper tools to fix this.

One BO has a very long SetStatus(...) method that goes through a long series of checks, and any time it finds something wrong -- which could entail very complex property interactions -- it would display an error message and return false.

After much lost hair, I figured out I could port this directly almost line for line with this modified approach:

I created a StatusSetter object with an ErrorMessage property and a very simple validation rule: If this property has any value, the object is broken and the ErrorMessage tells you why. Otherwise, the object is fine. 

The StatusSetter has a Create() method that takes the parameters passed to the old SetStatus() method and stores them as properties.  The DataPortal_Update code is almost line for line equivalent to the old Visual C+ 6.0 code, except that instead of displaying a MessageBox, it just sets the ErrorMessage property and breaks the object.

Now I have a direct replacement that works exactly like the previous code, and the UI can decide how to display the error rather than having MessageBoxes in the BO.

Now, you'd never design a CSLA application like this from scratch -- this is just to illustrate how flexible it can be. I'd much rather associate the various broken rules with the properties that were passed in, but when you're doing a port like this sometimes you have to keep the logic in place until you understand it enough to refactor it into separate rules.

ajj3085 replied on Thursday, August 07, 2008

I had something similar, in my program quotes are converted to orders, which are converted to invoices,etc.

Rocky's suggestion was to create a "conversion" object, who's responsibility was to check the object to ensure it's valid to move to the next stage.  It's been working rather well for me.

You're right though.. if you have a lot of conversions you'll wind up with a lot of classes.  But it will work, and it will be maintainable, which is the goal.

rsbaker0 replied on Thursday, August 07, 2008

^^^^

Yes, this approach has worked well for us also, but you don't necessarily have to have one object for each stage.

We have objects that can be in one of 10 states or so, and have a transition table that indicates what state transitions are valid (as well as some other qualifying requirements or prerequisites for the state change).

A single validation rule can manage whether a particular state change is allowed.

Copyright (c) Marimer LLC