Delete and Post Validation Rules

Delete and Post Validation Rules

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


stanc posted on Thursday, July 19, 2007

I've quickly checked the forum looking for some post on this, but haven't found any. I was wondering if anyone has used/extended validation rules beyond save validations. We have a project where there will be all sorts of validation rules for deletes and posting an item.

I was thinking about possibly extending the Validation rules to cover this, but was wondering how others may have attacked this problem. Another idea I had was to just add my validations as part of the data portal call for the delete.

This raises a separate but related problem concerning Posting. Should I extend data portal calls to include a new one for Posting (DataPortal_Post) or should I just add a flag in my business object that gets set before calling the save letting me know to post?

Any input would be greatly appreciated.

Stan

RockfordLhotka replied on Thursday, July 19, 2007

If you want to do a final rule check before an insert/update/delete, you can override Save() to do that check - on a root object anyway.

On a child object that's harder, and the only place you can really do such a final check is in the DP_Insert/Update/Delete methods. Nothing is called on child objects immediately before saving...

stanc replied on Thursday, July 19, 2007

OK. That is basically what I thought would be the best idea.

Do you have any opinions on how I should handle the Post action? It may help if I defined Post from our application: Posting is really just a status change and has stricter rules imposed on it. For example it may be ok to save an object with a certain property filled in, but it has to be filled in to post the object. But it also performs other actions. In our case it's basically an inventory system, and upon saving an item the inventory isn't touched but after posting we remove the items from inventory. Do you think adding a DP_Post would be a good idea in this case? Or should I just use a flag noting the item is being posted and handle it on the DP_Update?

Thank you for the input and the framework. Although I am new to CSLA, I already see a great deal of value in it for my group and myself.

RockfordLhotka replied on Thursday, July 19, 2007

Posting sounds like a different use case from "adding" or "editing". As such, you should start out with the assumption that you need a different object for that different use case.

Only if, after doing your design, you discover that the object needed by the Post use case is identical to the object from the Add/Edit use case should they be the same object.

From your description, they are most certainly different objects.

In fact, it sounds like your Post use case has at least a couple objects of its own:

Neither of which are objects from the Add/Edit use case.

stanc replied on Thursday, July 19, 2007

OO! Really just starting to get a grasp on it, and this our first project fully utilizing the principals.

With that said, I just want to ask one more follow up after providing you a little more information. The reason I see it as part of the same object is because the user may go straight from the data entry to posting the object without even a save in between. Or even more commonly they will open the object make some modifications and then post it. From a UI side this will all be accessible from the same screen as data entry.

I know you don't know all the details of the Use Cases, but I am just curious if the additional information changed your opinion any?

Thanks again for your help.

sgraham replied on Wednesday, July 25, 2007

I have two very similar situations.

First situation, I don't want to do a validation that requires a database hit until the user is ready to save the object...a child object, I  might add.

Second situation, I want a workflow type scenario like you mention (preparing and saving an object prior to submitting/posting it).

Please advise anyone!!

RockfordLhotka replied on Wednesday, July 25, 2007

Let's talk about the second first (just for fun).

A workflow is a series of activities. Each activity is independent, and in fact should be thought of as its own use case. So each workflow activity, in a normal case, will have its own objects that implement that activity.

So your prepare/save activity will have different objects from submit/post because those are different actvities at different points in the workflow.

 

The first situation can be solved a couple different ways.

The simplest way is to call or implement the rules in your Friend/internal Insert()/Update() methods, and then throw an exception in case of failure. You have to throw an exception at this point, because other objects will have already committed to the database and you need the overall transaction to roll back.

The harder, but more elegant way, is to do what I described earlier (in this or another thread, don't remember), where you use rule priorities to prevent those final rules from running until you are actually saving the object.

This isn't really that hard, but it is a bit of extra work. The trick is that you need to trigger checking of all rules for the children of the root, from wthin the root object's Save() method. This must happen before calling MyBase.Save() or base.Save().

So what you need to do is simply cascade a CheckFinalRules() call down through all children. Using the priority scheme, each object (root or child) will have a private field like _checkFinal, which will normally be false, but now will be true. So CheckFinalRules() will look like this:

internal void CheckFinalRules()
{
  _checkFinal = true;
  ValidationRules.CheckRules();

  // check rules for children
  _childList.CheckFinalRules();
}

And if your object is a collection it would look like this:

internal void CheckFinalRules()
{
  foreach (Child child in deletedList)
    ValidationRules.CheckRules();
  foreach (Child child in this)
    ValidationRules.CheckRules();
}

Again, for this to work, you need to use rule priorities:

  1. Your normal rules are at priority 0 (the default)
  2. For each property you insert a rule at priority 1 that always returns true, but sets e.StopProcessing to true if _checkFinal is false
  3. Your final rules are at priority 2
  4. You must set the process through priority threshold value to 2

Obviously some of this work can be done in your custom base classes (you do have them right? Wink [;)]), because you can put basic CheckFinalRules() implementations in the base classes, and you can set the process through priority threshold change in the base class.

If you aren't adverse to a bit of reflection, you could even automatically insert the _checkFinal short-circuiting rule (step 2) for each property using code in the base class.

sgraham replied on Thursday, July 26, 2007

First off, thanks for the rapid response.

Now, excuse my learning curve.  I'm catching on..but slowly.

First, regarding the separate use cases being separate objects:  as the previous poster also mentioned, the GUI, and 95% of the business logic is the same between the save and submit functions (both need the same properties, rules and data access).  So, does it make sense to leverage that code somehow (with some OO technique) or create completely different objects?  Maybe I'm missing something but it seems complex to maintain near duplicate objects, right?  Would you try to leverage the overlap?  If so, how?

Second, I understand the solution you suggested and I like it!  I don't mean to get off topic but I'm curious about your comment about having custom base classes.  Could you elaborate on that?  I'm a somewhat "experienced" developer (several years) who's trying to become more experienced with OO techniques and strategies so that I can guide others in our company (since the company is not at all OO savy).  Thanks!

Sean

 

RockfordLhotka replied on Thursday, July 26, 2007

sgraham:

Second, I understand the solution you suggested and I like it!  I don't mean to get off topic but I'm curious about your comment about having custom base classes.  Could you elaborate on that?  I'm a somewhat "experienced" developer (several years) who's trying to become more experienced with OO techniques and strategies so that I can guide others in our company (since the company is not at all OO savy).  Thanks!

As a general rule I recommend that people always create a set of custom base classes that inherit from the CSLA .NET base classes. The primary extensibility mechanism for CSLA .NET is to override methods to customize behavior - and you typically want to do that in a central place.

So I suggest you create base classes (even if they are otherwise empty), and inherit from your base classes in your objects, rather than inheriting from CSLA directly.

namespace MyBaseClasses
{
  public abstract class BusinessBase<T> : Csla.BusinessBase<T>
    where T: BusinessBase<T>
  { }
  // ...
}

Namespace MyBaseClasses
  Public MustInherit Class BusinessBase(Of T As BusinessBase(Of T))
    Inherits Csla.BusinessBase(Of T)
  End Class
  ' ...
End Namespace

If you do this for BusinessBase, BusinessListBase, ReadOnlyBase, ReadOnlyListBase, NameValueListBase and EditableRootListBase you allow yourself to customize behaviors over time by overriding (or adding) methods in your base classes, thus affecting all your business objects.

ajj3085 replied on Thursday, July 26, 2007

sgraham:
First, regarding the separate use cases being separate objects:  as the previous poster also mentioned, the GUI, and 95% of the business logic is the same between the save and submit functions (both need the same properties, rules and data access).  So, does it make sense to leverage that code somehow (with some OO technique) or create completely different objects?  Maybe I'm missing something but it seems complex to maintain near duplicate objects, right?  Would you try to leverage the overlap?  If so, how?


There are a few ways to handle this.  One you could create an internal class which the two public BOs use so that the shared behavior is encapsulated there.  The other option is to create a base class, and have your BOs inherit their behavior from the base class.  You can do that if you don't expect the shared logic to change or if the changes will always have to affect both classes.

sgraham:
Second, I understand the solution you suggested and I like it!  I don't mean to get off topic but I'm curious about your comment about having custom base classes.  Could you elaborate on that?  I'm a somewhat "experienced" developer (several years) who's trying to become more experienced with OO techniques and strategies so that I can guide others in our company (since the company is not at all OO savy).

You should implement subclasses which match BB, BLB, ERLB, etc.  Then your BOs subclass these new subclasses.  Your customer business base classes need not add any behavior, but if at some point you require all your BOs to have a certain behavior, you can add it to your base classes.  This lets you avoid modifying the Csla framework itself.

HTH
Andy

RockfordLhotka replied on Thursday, July 26, 2007

sgraham:

First, regarding the separate use cases being separate objects:  as the previous poster also mentioned, the GUI, and 95% of the business logic is the same between the save and submit functions (both need the same properties, rules and data access).  So, does it make sense to leverage that code somehow (with some OO technique) or create completely different objects?  Maybe I'm missing something but it seems complex to maintain near duplicate objects, right?  Would you try to leverage the overlap?  If so, how?

The key is to factor your objects to avoid coupling and to get reuse if you can. Reuse is not the dominant goal! Maintainability is the goal, and improper reuse leads to coupling, which is (arguably) the ultimate evil.

So if your two use cases both allow the user to enter/alter all the same data, and the only difference is that the edit use case and post use case set a different flag in the database, then I'd suggest all you need is a little logic in your DP_Update() method to accomodate the difference. This presupposes that the properties that can be changed, and the rules governing those properties are substantively the same.

But often the edit use case is where the user gets to alter values, and the post use case is quite different: the user merely selects the item to post, maybe gets to alter a subset of values and then the post occurs. In this case the use cases are quite different and need different objects.

The problem with reusing objects across use cases, is that you effectively couple the use cases. So later, when you change the requirements (and thus the code) for one use case, you run the serious risk of breaking the other use case(s). This kind of fragility is the result of coupling, and is why I say coupling is the ultimate evil.

Worse, notice how the loss of maintainability flowed from reuse!! We were all (at least I was) taught that reuse was this wonderful thing, when in reality it has a huge downside that often overwhelms any advantage the reuse might have provided.

"Quicker, easier, more seductive" is the dark side, but not stronger Smile [:)]

Copyright (c) Marimer LLC