Broken Rules

Broken Rules

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


Reactance posted on Wednesday, September 20, 2006

Hi,

Ive implmented a simple rule in my BO when i attempted to save my BO it says that my object is invaild in the catch handler i understand the reason why its doing this, but how do i know which field in the BO is invaild ??

try

{
   //Save Bo here....

}

Catch(Exception e)

{

// BO is invaild display which field is invaild

}

How did u guys implmented it ???

Thank you in advance.

Michael Hildner replied on Wednesday, September 20, 2006

Hi Reactance,

I recently subclassed BusinessBase. In there I made a public property that's a string of all the broken rules:

/// <summary>

/// Gets the broken rules for this object.

/// </summary>

public virtual string BrokenRulesMessage

{

get

{

return BuildBrokenRulesMessage();

}

}

/// <summary>

/// Builds the broken rules message string.

/// </summary>

/// <returns></returns>

private string BuildBrokenRulesMessage()

{

StringBuilder sb = new StringBuilder();

foreach (BrokenRule rule in this.BrokenRulesCollection)

{

sb.Append(rule.Description);

sb.Append(Environment.NewLine);

}

return sb.ToString();

}

Then when I try to save, you can do something like

catch (Csla.Validation.ValidationException)

{

string message = temp.BrokenRulesMessage;

MessageBox.Show(message);

}

I'm not that well versed with CSLA, just thought I'd throw out what I did.

Mike

Reactance replied on Wednesday, September 20, 2006

Thank you for the speedy reply i`ll look in it tonight when i get home !

Is there anyone else with another way of doing this ?

:-)

 

mr_lasseter replied on Wednesday, September 20, 2006

This is already implemented in CSLA by using the following:

BO.BrokenRulesCollection.ToString

Michael Hildner replied on Wednesday, September 20, 2006

Well... duh! Whoops.

david.wendelken replied on Tuesday, October 17, 2006

Michael,

I'm interested in what you had to do in order to subclass BusinessBase.

Did you subclass the other major classes also?  BusinessListBase, ReadOnlyBase, etc.?

I'm trying to do that in order to add the broken rules collection as a public property plus the full rule list as a public property.

I started with BusinessBase and BusinessListBase subclasses.

All I wanted to do was the minimum necessary, so I defined the abstract classes and added one public BrokenRules property.

Then I went into ProjectTracker.Library and modified the Role and Roles classes to subclass my classes instead. 

It's complaining that the Save method needs to return my subclass instead of Roles.  I sure don't want to have to override every darn method in each one of the classes!  Is there a simpler way?

 

 

Michael Hildner replied on Wednesday, October 18, 2006

David,

There was a thread 2 or 3 weeks ago where a bunch of code was posted showing examples of subclassing all the base classes - 6 in 2.0.3, and 7 in 2.1.

Anyway, here's an example of BusinessBase. I added in those two methods, take them out if you want the bare minimum.

namespace Buoy.BusinessObjects20

{

[Serializable()]

public abstract class BuoyBusinessBase<T> :

BusinessBase<T>

where T : BuoyBusinessBase<T>

{

/// <summary>

/// Marks the object as clean. Should be used when creating a new

/// object and you don't want it saved unless the user actually

/// changes something.

/// </summary>

public virtual void MarkAsClean()

{

this.MarkClean();

}

/// <summary>

/// Gets the broken rules for this object.

/// </summary>

public virtual string BrokenRulesMessage

{

get

{

return this.BrokenRulesCollection.ToString();

}

}

 

}

}

Not sure what's happening with that save business.

Regards,

Mike

Edit: Yes, I subclassed all of those classes. That way you're set up to make your modifications, if need be. It's the recommended approach.

ajj3085 replied on Wednesday, October 18, 2006

Just wanted to point out that the subclass needn't make any change to the API at all; mine are simply this:

[Serializable]
public abstract class MyBusinessBase<T> :
    Csla.BusinessBase<T> where T : MyBusinessBase<T> { }

david.wendelken replied on Wednesday, October 18, 2006

BusinessBase seemed to work ok for me.

But BusinessListBase is where I had the problem.

 

Michael Hildner replied on Wednesday, October 18, 2006

Here's my BusinessListBase

namespace Buoy.BusinessObjects20

{

[Serializable]

public abstract class BuoyBusinessListBase<T, C> :

BusinessListBase<T, C>

where T : BuoyBusinessListBase<T, C>

where C : Csla.Core.IEditableBusinessObject

{

}

}

david.wendelken replied on Wednesday, October 18, 2006

Found the other thread on subclassing...

 

http://forums.lhotka.net/forums/2/6596/ShowThread.aspx

david.wendelken replied on Thursday, October 26, 2006

Ok, I've had a partial success on subclassing the main csla classes and publically exposing the broken rules collection. 

I'm using Role, Roles, and EditRoles from PTWeb for my test case, in order to determine what changes to the "standard" way of coding things need to be done.

With the concept that a rule can be in a warning state, it's possible for an object that has just been loaded will have broken rules.  (Ditto if we just changed the rules in the object but didn't update the data in the database, but that's another topic. :)

So, I added ValidationRules.CheckRules() to the end of the private constructor in Role so that the object would check its rule status upon being created.   I also added a max string length of 10 rule to the Name property so some of the existing Roles would be considered broken.

It blew up.  (Details of error at end of message.)

I changed it to ValidationRules.CheckRules("Name") and it worked perfectly.

Turns out the problem is being caused by the NoDuplicates instance rule on the Id property.

Ok, so maybe it's in a state of flux being halfway thru the constructor of a just added (or about to be added) Role.  Option 2 was to loop thru each Role in the Roles collection and ask it to check its rules.  Not ideal, but hey.  Can't do it though, the ValidationRules.CheckRules() is private.

I may be able to override the CheckRules and make it accessible to its parent, or just fake it with another method to do just that, but frankly, it seems that the real problem is with CheckRules() or the implementation of the NoDuplicates rule.  It should just work. :(

If you have ideas, feel free to pass them on.  I'll keep working on it.

Here's the error I get:

Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

Line 99:     {
Line 100:      Roles parent = (Roles)this.Parent;
Line 101:      foreach (Role item in parent)
Line 102:        if (item.Id == _id && !ReferenceEquals(item, this))
Line 103:        {

Source File: C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Role.cs    Line: 101

Stack Trace:

[NullReferenceException: Object reference not set to an instance of an object.]
   ProjectTracker.Library.Admin.Role.NoDuplicates(Object target, RuleArgs e) in C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Role.cs:101
   Csla.Validation.RuleMethod.Invoke(Object target) in C:\Visual Studio Projects\Csla20cs\Csla\Validation\RuleMethod.cs:96
   Csla.Validation.ValidationRules.CheckRules(List`1 list) in C:\Visual Studio Projects\Csla20cs\Csla\Validation\ValidationRules.cs:658
   Csla.Validation.ValidationRules.CheckRules() in C:\Visual Studio Projects\Csla20cs\Csla\Validation\ValidationRules.cs:630
   ProjectTracker.Library.Admin.Role..ctor(SafeDataReader dr) in C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Role.cs:154
   ProjectTracker.Library.Admin.Role.GetRole(SafeDataReader dr) in C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Role.cs:134
   ProjectTracker.Library.Admin.Roles.DataPortal_Fetch(Criteria criteria) in C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Roles.cs:144

[CallMethodException: DataPortal_Fetch method call failed]
   Csla.MethodCaller.CallMethod(Object obj, MethodInfo info, Object[] parameters) in C:\Visual Studio Projects\Csla20cs\Csla\DataPortal\MethodCaller.cs:118
   Csla.Server.SimpleDataPortal.Fetch(Type objectType, Object criteria, DataPortalContext context) in C:\Visual Studio Projects\Csla20cs\Csla\DataPortal\Server\SimpleDataPortal.cs:109

[DataPortalException: DataPortal.Fetch failed (System.NullReferenceException: Object reference not set to an instance of an object.
   at ProjectTracker.Library.Admin.Role.NoDuplicates(Object target, RuleArgs e) in C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Role.cs:line 101
   at Csla.Validation.RuleMethod.Invoke(Object target) in C:\Visual Studio Projects\Csla20cs\Csla\Validation\RuleMethod.cs:line 96
   at Csla.Validation.ValidationRules.CheckRules(List`1 list) in C:\Visual Studio Projects\Csla20cs\Csla\Validation\ValidationRules.cs:line 658
   at Csla.Validation.ValidationRules.CheckRules() in C:\Visual Studio Projects\Csla20cs\Csla\Validation\ValidationRules.cs:line 630
   at ProjectTracker.Library.Admin.Role..ctor(SafeDataReader dr) in C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Role.cs:line 154
   at ProjectTracker.Library.Admin.Role.GetRole(SafeDataReader dr) in C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Role.cs:line 134
   at ProjectTracker.Library.Admin.Roles.DataPortal_Fetch(Criteria criteria) in C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Roles.cs:line 144)]
   Csla.DataPortal.Fetch(Type objectType, Object criteria) in C:\Visual Studio Projects\Csla20cs\Csla\DataPortal\Client\DataPortal.cs:192
   Csla.DataPortal.Fetch(Object criteria) in C:\Visual Studio Projects\Csla20cs\Csla\DataPortal\Client\DataPortal.cs:140
   ProjectTracker.Library.Admin.Roles.GetRoles() in C:\Visual Studio Projects\ProjectTracker20cs\ProjectTracker.Library\Admin\Roles.cs:86
   RolesEdit.GetRoles() in c:\Visual Studio Projects\ProjectTracker20cs\www\PTWeb\RolesEdit.aspx.cs:68
   RolesEdit.RolesDataSource_SelectObject(Object sender, SelectObjectArgs e) in c:\Visual Studio Projects\ProjectTracker20cs\www\PTWeb\RolesEdit.aspx.cs:126
   Csla.Web.CslaDataSource.OnSelectObject(SelectObjectArgs e) in C:\Visual Studio Projects\Csla20cs\Csla\Web\CslaDataSource.cs:150
   Csla.Web.CslaDataSourceView.ExecuteSelect(DataSourceSelectArguments arguments) in C:\Visual Studio Projects\Csla20cs\Csla\Web\CslaDataSourceView.cs:94
   System.Web.UI.DataSourceView.Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback) +17
   System.Web.UI.WebControls.DataBoundControl.PerformSelect() +149
   System.Web.UI.WebControls.BaseDataBoundControl.DataBind() +70
   System.Web.UI.WebControls.GridView.DataBind() +4
   System.Web.UI.WebControls.BaseDataBoundControl.EnsureDataBound() +82
   System.Web.UI.WebControls.CompositeDataBoundControl.CreateChildControls() +69
   System.Web.UI.Control.EnsureChildControls() +87
   System.Web.UI.Control.PreRenderRecursiveInternal() +41
   System.Web.UI.Control.PreRenderRecursiveInternal() +161
   System.Web.UI.Control.PreRenderRecursiveInternal() +161
   System.Web.UI.Control.PreRenderRecursiveInternal() +161
   System.Web.UI.Control.PreRenderRecursiveInternal() +161
   System.Web.UI.Control.PreRenderRecursiveInternal() +161
   System.Web.UI.Control.PreRenderRecursiveInternal() +161
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1360

 

david.wendelken replied on Thursday, October 26, 2006

I re-wrote the NoDuplicates rule as follows:

private bool NoDuplicates(object target, Csla.Validation.RuleArgs e)
{
   
Roles parent = (Roles)this.Parent;
  if (parent != null)
  {
     foreach (Role item in parent)
    {
         if (item.Id == _id && !ReferenceEquals(item, this))
        {
            e.Description =
"Role Id must be unique";
            return false;
        }
     }
  }
  return true;
}

Basically, the original rule assumed that this.Parent would return something.  I added a check to make sure that it did before I tried to verify that there were no duplicates.

Copyright (c) Marimer LLC