Business Rules Applying To Multiple Properties

Business Rules Applying To Multiple Properties

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


Chris62 posted on Wednesday, December 28, 2011

I have a biz object where I need to check that Work has been assigned to either a person(UserId) or a group of people(Group), but not both.  I'm having problems when I try to assign a user(using test cases to test my work).  Below is my code(some parts snipped out for clarity).

 

[Serializable]

public partial class Work : BusinessBase<Work>

{

private static readonly PropertyInfo<System.Int32?> UserIdAssignToProperty = RegisterProperty<System.Int32?>(p => p.UserIdAssignTo, "User Id Assign To");

        public System.Int32? UserIdAssignTo

        {

            get { return GetProperty(UserIdAssignToProperty); }

            set { SetProperty(UserIdAssignToProperty, value); }  // error is happening here

        }

private static readonly PropertyInfo<Group> GroupProperty = RegisterProperty<Group>(p => p.GroupAssignTo, "Group Assign To", Csla.RelationshipTypes.LazyLoad);

        public Group GroupAssignTo

        {

            get

            {

                bool cancel = false;

                OnChildLoading(GroupProperty, ref cancel);

 

                if (!cancel)

                {

                    if(!FieldManager.FieldExists(GroupProperty))

                    {

                            LoadProperty(GroupProperty, Group.GetByWorkChild(this));

                    }

                }

                return GetProperty(GroupProperty); 

            }

            protected set { SetProperty(GroupProperty, value); }

        }

 

protected override void AddBusinessRules()

{

base.AddBusinessRules();

BusinessRules.AddRule(new AssignWork(UserIdAssignToProperty, GroupProperty));

BusinessRules.AddRule(new AssignWork(GroupProperty, UserIdAssignToProperty));

BusinessRules.AddRule(new AnotherBizRule(UserIdAssignToProperty));

}

// parent factory methods

// parent data portal

}

 

[Serializable]

public partial class Group : BusinessBase<Group>

{

private static readonly PropertyInfo<System.Int32?> IdProperty = RegisterProperty<System.Int32?>(p => p.Id, "Group Id");

        public System.Int32? Id

        {

            get { return GetProperty(IdProperty); }

            set { SetProperty(IdProperty, value); }

        }

private static readonly PropertyInfo<System.String> NameProperty = RegisterProperty<System.String>(p => p.Name, "Group Name");

        public System.String Name

        {

            get { return GetProperty(NameProperty); }

            set { SetProperty(NameProperty, value); }

        }

// child factory methods

// child data portal methods

}

 

internal class AssignWork : BusinessRule

{

public AssignWork(IPropertyInfo PrimaryProperty, IPropertyInfo SecondaryProperty)

            : base(PrimaryProperty)

{

if (InputProperties == null)

{

InputProperties = new List<IPropertyInfo>();

}

InputProperties.Add(PrimaryProperty);

InputProperties.Add(SecondaryProperty);

}

protected override void Execute(Csla.Rules.RuleContext context)

{

base.Execute(context);

 

IPropertyInfo groupProp = InputProperties.Find(prop => prop.FriendlyName.Equals("Group Assign To"));

IPropertyInfo userProp = InputProperties.Find(prop => prop.FriendlyName.Equals("User Id Assign To"));

 

var userId = (dynamic)context.InputPropertyValues[userProp];

var group = (dynamic)context.InputPropertyValues[groupProp];

 

if (userId != null && (group != null && group.Id == null))

{

context.AddErrorResult(String.Format("Either assign to {0} or {1}, not both", userProp.FriendlyName, groupProp.FriendlyName));

}

 

}

}

 

internal class AnotherBizRule : BusinessRule

{

public AnotherBizRule(IPropertyInfo PrimaryProperty, IPropertyInfo SecondaryProp)

            : base(PrimaryProperty) 

{

if (InputProperties == null)

{

InputProperties = new List<IPropertyInfo>();

}

InputProperties.Add(PrimaryProperty);

}

protected override void Execute(Csla.Rules.RuleContext context)

{

base.Execute(context);

 

// do some biz rule on the UserId property, it works in this rule

 

}

}

 

// Unit Test  Case (mbUnit)

[Test]

public void Test1()

{

Work myWork = Work.NewWork();

myWork.UserIdAssignTo = 123; // errors here

myWork.Group = SomeGroup;

// check BrokenRulesCollection to see if I caught the broken rule(s)

}

 

[Test]

public void Test2()

{

Work myWork = Work.NewWork();

myWork.Group = SomeGroup;

myWork.UserIdAssignTo = 123; 

// check BrokenRulesCollection to see if I caught the broken rule(s)

}

Any help/suggestions would be greatly appreciated.

Chris62 replied on Wednesday, December 28, 2011

Forgot to include the error I was getting:

Error from Vis. Studio:

Property load or set failed for property UserIdAssignTo (Property get not allowed)

Inner Exception: {"Property get not allowed"}

Stack Trace:

   at Csla.Core.BusinessBase.ReadProperty(IPropertyInfo propertyInfo)

   at Csla.Core.BusinessBase.Csla.Core.IManageProperties.ReadProperty(IPropertyInfo propertyInfo)

   at Csla.Rules.BusinessRules.RunRules(IEnumerable`1 rules)

   at Csla.Rules.BusinessRules.CheckRulesForProperty(IPropertyInfo property, Boolean cascade)

   at Csla.Rules.BusinessRules.CheckRules(IPropertyInfo property)

   at Csla.Core.BusinessBase.PropertyHasChanged(IPropertyInfo property)

   at Csla.Core.BusinessBase.LoadPropertyValue[P](PropertyInfo`1 propertyInfo, P oldValue, P newValue, Boolean markDirty)

   at Csla.Core.BusinessBase.SetProperty[P](PropertyInfo`1 propertyInfo, P newValue, NoAccessBehavior noAccess)

JonnyBee replied on Thursday, December 29, 2011

The error is connected with Lazy loading of fields.

  1. A rule should NOT trigger lazy loading when a field has not been initialized yet.
  2. BusinessObjects will throw an exception if your code (here rule) tries to read the field value before it has been initalized.

When your rule is using a lazy loaded field you must create the rule as an inner rule inside your BO so the rule can have access to FieldDataManager and check if the value is created or not. You cannot use the InputProperties to specify the lazy field as an input to the rule.

As a side note:

I am curios about the relationship between Work and Group. Is it a

This is described in the  UsingCsla4-02-Objects ebook from Rocky and have slightly different property declaration syntax. 

From what I can see from your code - I'd rather have GroupId and GroupName as properties of the Work object rather than having an owning lazy loaded Group object. (and a rule that will lookup and set GroupName asyncronously when GroupId is set).

Chris62 replied on Thursday, December 29, 2011

Thanks for your help JonnyBee, I originally had Group as GroupId, but figured I'd make "Group" a child object to the Parent "Work".  I went ahead and switched back to a GroupId(I really don't need the GroupName to persist to the db).

 

I think I need to go back and reread the Csla4-02 Objects book, my understanding may not be where it needs to be...

Copyright (c) Marimer LLC