BusinessListBase validation - only one child with property == 'xyz'

BusinessListBase validation - only one child with property == 'xyz'

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


FreeAsInBeer posted on Wednesday, May 09, 2007

Hi all,

just a quick question, if you don't mind.

I've got a BusinessListBase, and its children have a property (string) called Location. (this is abstract/made up, so bare with me please)

I need to ensure that there are no duplicate Locations in the collection.

I know I can access the parent from within the children, and when the Location property changes I can do a test... but... if I do find a duplicate (ie a child in the list already with that Location) how can I add a validation error/broken rule to the child that has just had its Location changed?

My other thought was to have the BusinessListBase contain the validation error, but, can/does a BusinessListBase have its own BrokenRulesCollection, or does it just 'get' them from the children?

thanks in advance
FaiB

jason2000 replied on Wednesday, May 09, 2007

there is a similar use case in ProjectTracker:there are no more 5 resouces in a project.


      private static bool No5Resources<T>(
         T target, Csla.Validation.RuleArgs e) where T : Project
      {
          if (target.Resources.Count>5)
          {
              e.Description = "Already 5 resources in the project!!";
              return false;
          }
          return true;
      }

        void Resources_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
        {
     //bind the validation to Name Property  
            PropertyHasChanged("Name");
        }


 //if the project object is retrieved from database
        protected override void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
        {
            this.Resources.ListChanged -= new System.ComponentModel.ListChangedEventHandler(Resources_ListChanged);
            this.Resources.ListChanged += new System.ComponentModel.ListChangedEventHandler(Resources_ListChanged);
            base.OnDeserialized(context);
        }

    //if the project object is newly created 
    protected override void DataPortal_Create()
    {
      _id = Guid.NewGuid();
      _started.Date = DateTime.Today;
      this.Resources.ListChanged += new System.ComponentModel.ListChangedEventHandler(Resources_ListChanged);
      ValidationRules.CheckRules();
    }

all these code in project calss.

JoeFallon1 replied on Thursday, May 10, 2007

I often put those kind of rules in the Root BO that contains the collection. Then the root BO can ask the collection questions like "How many resources do you have?" Or "what are your locations and are any of them duplicates". The root BO can keep track of which child object broke the rules and expose a broiken rule accordingly.

I will usually call these extra rules in IsValid just before getting the real value for IsValid - this gives them a chance to become broken or not one last time. I may sometimes write a lengthy procedure to figure this out and pass in stringbuilder and then append each problem to it so the full message is returned to the calling method.

Joe

 

FreeAsInBeer replied on Thursday, May 10, 2007

JoeFallon1:

I often put those kind of rules in the Root BO that contains the collection. Then the root BO can ask the collection questions like "How many resources do you have?" Or "what are your locations and are any of them duplicates". The root BO can keep track of which child object broke the rules and expose a broiken rule accordingly.



The problem is i dont have a root BO, just a standalone BusinessListBase. Can a BLB have its own BrokenRulesCollection?

thanks jason2000, I'll have a play with that and see if it does what I need.

RockfordLhotka replied on Thursday, May 10, 2007

Your child objects have a protected Parent property, through which you can access the root list. Using this, you can implement a rule method to check for duplication. Typically you'd attach this rule to your primary identity property in the child class.

jtgooding replied on Wednesday, May 30, 2007

In my BusinessListBase I have added a AddConstraintCheck virtual method that gets called on any add/set, and a DeleteConstraintCheck that gets called on any delete/remove. This could easily be expanded to use delegates to have multiple reusable checks but in all my cases this simpler aproach works.

Note that I don't throw an exception, I leave that to the AddConstraintCheck method, that way if duplicates are expected and to be ignored the developer can choose to not throw an exception.

public new void Add(C item)
{
    item.MarkAsChild();
    C OldItem = FindDeleted(item);
    if (OldItem == null)
    {
        if (AddConstraintCheck(item))
        {
            base.Add(item);
        }
        else
        {
            if (AddConstraintCheck(OldItem))
                UnDeleteChild(OldItem);
        }
    }
    item.IsDirtyChanged +=
new EventHandler(item_IsDirtyChanged);
    OnIsDirtyChanged();
}

protected virtual bool AddConstraintCheck(C item)
{
    return true;
}

RockfordLhotka replied on Wednesday, May 30, 2007

Unfortunately that solution isn't safe, because there are several other ways to get items into a collection beyond the Add() method. And in any case, shadowing a method is something that should be avoided if at all possible.

The better solution is to override the InsertItem() and SetItem() methods. These are invoked by the base collection class when an item is inserted (in any way) or replaced within your collection.

These methods are invoked during the add process - before the item has been added. If you do not call base.InsertItem() or base.SetItem() the add won't occur, and if you do cascade the call then the add will complete.

But these methods are the location where you should invoke your validation method, because then you are sure to have your code run in all cases.

jtgooding replied on Thursday, May 31, 2007

Yeah I realized after I posted that what I posted was incomplete, my BusinessListBase has a lot of additional functionality and seperating the other stuff out I overload AddNewCore to provide polymorphic core creation when polulating a list from a database and a whole bunch of other stuff.

In my code I handle InsertItem and SetItem, I've just been knocking my head against the wall trying to figure out what broke with 3.0 in BusinessListBase and the EditLevel, since I have so many updates its difficult to tell if it's me or the beta, my 2.0 based BusinessListBase works perfectly.

I should start a different thread, but when a BusinessListBase class is a child of a BusinessBase, when AcceptChanges is called on the BusinessBase class the AcceptChanges of the child BusinessListBase class always throws an exception that it is not at the same edit level (it always appears to be zero at the acceptchanges call).  The other nested BusinessBase objects all are at the right EditLevel, so trying to hunt this down to see if its me or not.

John

RockfordLhotka replied on Thursday, May 31, 2007

What changed in 3.0 is that I added this new exception that you are seeing. The reason for adding the exception is to help troubleshoot bugs around edit level handling, because if you don’t find them then they mess up data binding in very-hard-to-diagnose ways.

 

If you got the code from svn then it should be working. My tests (unit and otherwise) include using a BLB as a child of a BB, and my tests all pass.

 

Rocky

 

From: jtgooding [mailto:cslanet@lhotka.net]
Sent: Thursday, May 31, 2007 7:43 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] BusinessListBase validation - only one child with property == 'xyz'

 

Yeah I realized after I posted that what I posted was incomplete, my BusinessListBase has a lot of additional functionality and seperating the other stuff out I overload AddNewCore to provide polymorphic core creation when polulating a list from a database and a whole bunch of other stuff.

In my code I handle InsertItem and SetItem, I've just been knocking my head against the wall trying to figure out what broke with 3.0 in BusinessListBase and the EditLevel, since I have so many updates its difficult to tell if it's me or the beta, my 2.0 based BusinessListBase works perfectly.

I should start a different thread, but when a BusinessListBase class is a child of a BusinessBase, when AcceptChanges is called on the BusinessBase class the AcceptChanges of the child BusinessListBase class always throws an exception that it is not at the same edit level (it always appears to be zero at the acceptchanges call).  The other nested BusinessBase objects all are at the right EditLevel, so trying to hunt this down to see if its me or not.

John



Copyright (c) Marimer LLC