CSLA .NET

Vibrant discussion about CSLA .NET and using the framework to build great business applications.

Call DataPortal From A PropertyRule

rated by 0 users
Answered (Verified) This post has 1 verified answer | 9 Replies | 0 Followers

Top 500 Contributor
20 Posts
Chris62 posted on Tue, Feb 14 2012 10:30 AM

I have a PropertyRule that needs to call into the database for values to verify against and I'm having problems with the DataPortal.  I'm getting an "Object is not valid and can not be saved" error message, but it's erroring out before I get to the save section.  It's erroring on the PropertyRule call

This is the Parent Business Class

[Serializable]
public class BizObject : BusinessBase
{
private readonly static PropertyInfo IdProperty = RegisterProperty(p => p.Id, "Primary Key", 0);
public int Id
{
get { return GetProperty(IdProperty); }
private set { SetProperty(IdProperty, value); }
}
public readonly static PropertyInfo SomeOtherIdProperty = RegisterProperty(p => p.SomeOtherId, "Some Other Id", 0);
public int SomeOtherId
{
get { return GetProperty(IdProperty); }
set { SetProperty(SomeOtherIdProperty, value); }
}
public readonly static PropertyInfo AnotherIdProperty = RegisterProperty(p => p.AnotherId, "Another Id", 0);
public int AnotherId
{
get { return GetProperty(AnotherIdProperty); }
set { SetProperty(AnotherIdProperty, value); }
}
// normal factory methods for Parent object
protected override void AddBusinessRules()
{
base.AddBusinessRules();
BusinessRules.AddRule(new AdditionRule(SomeOtherIdProperty, new List() { AnotherIdProperty}));
}
// normal dataportal_xyz for Parent object
}

This is the Business Object that will retrieve the data from the data base

[Serializable]
public class AnotherBizObject : BusinessBase


{
// usual propeties, factories, data portals, etc...
public static int GetReturnId(int AnotherID)
{
GetReturn cmd = new GetReturn() { AnotherID = AnotherID };
cmd = DataPortal.Execute(cmd);
return cmd.ReturnId;
}

[Serializable]
private class GetReturn : CommandBase
{
public int AnotherID { get; set; }
public int ReturnId { get; set; }

protected override void DataPortal_Execute()
{
// call to database here, use AnotherID to search on, return ReturnId
ReturnId = 10;
}
}
}

Here's the business rule used in the Parent Object

[Serializable]
public class AdditionRule : PropertyRule
{
public AdditionRule(IPropertyInfo Primary, IEnumerable Properties) : base(Primary)
{
AffectedProperties.AddRange(Properties);
}

protected override void Execute(RuleContext context)
{
base.Execute(context);

int someOtherId = (int)ReadProperty(context.Target, BizObject.SomeOtherIdProperty);
int anotherID = (int)ReadProperty(context.Target, BizObject.AnotherIdProperty);
if (someOtherId > 0 && anotherID > 0)
{
int returnID = AnotherBizObject.GetReturnId(anotherID);

if (returnID != (someOtherId + anotherID))
{
context.AddErrorResult("Error");
}
}
}
}

It errors on the line "int returnID = AnotherBizObject.GetReturnId(anotherID);"  It doesn't like going through the dataportal.  Also this works running through Visual Studio(localhost), but it fails when I push up to a dev/test environment(which is making debugging a treat).

Answered (Verified) Verified Answer

Top 10 Contributor
1,813 Posts
Verified by Chris62

Use BrokenRules.GetAllBrokenRules method to get a list of all objects in you "tree" that has broken rules.

When IsSelfValid is true and IsValid is false then you have broken rules on the child objects and the object cannot be saved.

Jonny Bekkum, Norway CslaContrib Coordinator

All Replies

Top 500 Contributor
20 Posts
Chris62 replied on Tue, Feb 14 2012 10:32 AM

Here's the stack trace:

Object is not valid and can not be saved

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: Csla.Rules.ValidationException: Object is not valid and can not be saved

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[ValidationException: Object is not valid and can not be saved]
   Csla.BusinessBase`1.Save() +303
   Surveys.Controllers.SurveyController.Save(FormCollection controls) in C:\Code\SurveySystem\Main\Surveys\Surveys\Controllers\SurveyController.cs:107
   lambda_method(Closure , ControllerBase , Object[] ) +108
   System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17
   System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +208
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
   System.Web.Mvc.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12() +55
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +263
   System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +191
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
   System.Web.Mvc.Controller.ExecuteCore() +116
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
   System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
   System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
   System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
   System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8966925
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184

We're running CSLA 4.2.2.0

Top 10 Contributor
1,813 Posts
JonnyBee replied on Tue, Feb 14 2012 10:39 AM

Hi,

This should work just fine.

So what is the error message that you get from the AnotherBizObject?

You may also consider to inhert from Csla.Rules.PropertyRule and set

CanRunInCheckRules = true
CanRunOnServer = false
CanRunAsAffectedProperty = false

This will deny the rule from running in Data Access (serverside of data portal), allow in CheckRules() on client and deny as affected property from another property that was changed. The effect is that the rule only runs when the user edits the property in UI.

 

 

 

Jonny Bekkum, Norway CslaContrib Coordinator

Top 10 Contributor
1,813 Posts
JonnyBee replied on Tue, Feb 14 2012 10:44 AM

Hmm, I'd also rather have the rule call the GetReturn commandobject directly and not got to AntoherBizObject.

So I'd prefer to put this static factory code inside the public available GetReturn command object (following the Singe Responsibility pattern):

public static int GetReturnId(int AnotherID)
{
GetReturn cmd = new GetReturn() { AnotherID = AnotherID };
cmd = DataPortal.Execute(cmd);
return cmd.ReturnId;
}

Or explicitly specify the object type on DataPortal.Execute

public static int GetReturnId(int AnotherID)
{
GetReturn cmd = new GetReturn() { AnotherID = AnotherID };
cmd = DataPortal.Execute<GetReturn>(cmd);
return cmd.ReturnId;
}

From your error message it is clear that Execute (which actually calls Save) is called on a BusinessBase object and NOT the CommandBase object.

Jonny Bekkum, Norway CslaContrib Coordinator

Top 500 Contributor
20 Posts
Chris62 replied on Tue, Feb 14 2012 11:47 AM

I haven't been able to find any other error messages.  Either in the Event Viewer or the WCF trace log(turned on through web.config/system.diagnostics).

Top 500 Contributor
20 Posts
Chris62 replied on Tue, Feb 14 2012 11:53 AM

I (hopefully) changed it around to what you're suggesting:

[Serializable]
public class AnotherBizObject : BusinessBase


{
// usual propeties, factories, data portals, etc...
}

[Serializable]
public class GetReturn : CommandBase
{
public static int GetReturnId(int AnotherID)
{
GetReturn cmd = new GetReturn() { AnotherID = AnotherID };
cmd = DataPortal.Execute(cmd);
return cmd.ReturnId;
}
private readonly static PropertyInfo AnotherIDProperty = RegisterProperty(p => p.AnotherID, "Another Id");
public int AnotherID { get; set; }
private readonly static PropertyInfo ReturnIDProperty = RegisterProperty(p => p.ReturnId, "Return Id");
public int ReturnId { get; set; }

protected override void DataPortal_Execute()
{
// call to database here, use AnotherID to search on, return ReturnId
ReturnId = 10;
}
}

In the AdditionRule I changed it to:

// snip
int anotherID = (int)ReadProperty(context.Target, BizObject.AnotherIdProperty);

if (someOtherId > 0 && anotherID > 0)
{
int returnID = GetReturn.GetReturnId(anotherID);  // changed to use new object GetReturn instead

// snip

P.S. this isn't the actual code, I stripped out the other pieces that are working and this is a simplified example

Top 10 Contributor
1,813 Posts

Hi,

Ok, one more major problem.

Do not mix propertyinfo and unmanaged properties like this. Use the snippets from the support folder when you declare properties.

DataPortal will create a "clone" of the object before calling Execute (actually Save) and your fields will have a value of 0 (the defalt value).

Your properties must be declared like this:

private readonly static PropertyInfo ReturnIDProperty = RegisterProperty(p => p.ReturnId);
public int ReturnId
{
   get return ReadProperty(ReturnIDProperty);
   private set LoadProperty(ReturnIDProperty, value);
}

As in your DataPortal_Execute you try to get the unmanaged field value.
- the recommended way is to use managed properties.


The error message you get simply says that the BusinessObject has broken rules and is not valid for save.

You should inspect <BusinessObject>.BrokenRulesCollection to see which rules are broken for which fields and redirect to a new page to show the errors.

Do you use the CslaModelBinder (assuming that this is an ASP.NET MVC application)?

Jonny Bekkum, Norway CslaContrib Coordinator

Top 500 Contributor
20 Posts

Ok, I fixed most of this.  (Sorry for the delay, got pulled off for a prod issue).  Currently I write out to a file with any broken rules in it, and so far there are none(after fixing some table perms), yet .IsValid, and .IsSaveable are both testing false (.IsSelfValid is true) for the parent object, while BrokenRulesCollection.Count is 0.  

The parent may or may not be creating children based on properties being set(example: if SomePropertyId = 5, then create a child object using a property rule).

We're not using CslaModelBinder, this is a Survey website, the questions/answers will be different for each survey.

I put a couple of lines of code in the DataPortal_Insert() to write a line to a file on the server, and we're not getting to the server(probably the IsSaveable and/or IsValid being false).

protected override void DataPortal_Insert()
{
TextWriter here = new StreamWriter("C:\\dp_insert.txt", true);
here.WriteLine("insert");
here.Close();
// normal insert code here

Hopefully I haven't confused you Big Smile, and thanks for your help!!!

Top 10 Contributor
1,813 Posts
Verified by Chris62

Use BrokenRules.GetAllBrokenRules method to get a list of all objects in you "tree" that has broken rules.

When IsSelfValid is true and IsValid is false then you have broken rules on the child objects and the object cannot be saved.

Jonny Bekkum, Norway CslaContrib Coordinator

Top 500 Contributor
20 Posts
Chris62 replied on Fri, Feb 17 2012 10:49 AM

The GetAllBrokenRules really helped out.  From there I was able to dig through and locate the error(I missed setting a permission on a table).  It's now up and running.

 

Thanks again for your help(and patience)!!!

Page 1 of 1 (10 items) | RSS

Copyright (c) 2006-2010 Marimer LLC. All rights reserved.
Email admin@lhotka.net for support.
Powered by Community Server (Non-Commercial Edition), by Telligent Systems