Custom Business Rules working but Common Rules are not

Custom Business Rules working but Common Rules are not

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


LarzStarz posted on Tuesday, July 20, 2010

I'm working today with CSLA 4.0 RC1, and am struggling with getting BusinessRules on a basic object to work as I think they should.  I define my properties using PropertyInfo objects and override AddBusinessRules to add basic validation like Required and MaxLength to my string type properties.

The problem is that when I set the property values through unit tests, the rules from CommonRules are not validated.  Specifically, a property with a Required rule is set to an empty string, but does not trigger an item in BrokenRulesCollection.  If I create a derived BusinessRule to do the same check, it gets called as expected and the object is correctly marked as IsValid == false.

I'll post some code here to see if it helps someone else spot my issue.  Please let me know if you need additional code.  Thanks in advance.

Todd Larson

        private static PropertyInfo<int> FileTypeIDProperty = RegisterProperty<int>(typeof(FileType), new PropertyInfo<int>("FileTypeID"));

        private static PropertyInfo<string> FileTypeCodeProperty = RegisterProperty<string>(typeof(FileType), new PropertyInfo<string>("FileTypeCode"));

        private static PropertyInfo<string> DescriptionProperty = RegisterProperty<string>(typeof(FileType), new PropertyInfo<string>("Description"));

        private static PropertyInfo<string> ClassCodeProperty = RegisterProperty<string>(typeof(FileType), new PropertyInfo<string>("ClassCode"));

 

        private int _fileTypeId = FileTypeIDProperty.DefaultValue;

        private string _fileTypeCode = FileTypeCodeProperty.DefaultValue;

        private string _description = DescriptionProperty.DefaultValue;

        private string _classCode = ClassCodeProperty.DefaultValue;

public int FileTypeID

        {

            get { return GetProperty<int>(FileTypeIDProperty, _fileTypeId); }

        }

 

        public string FileTypeCode

        {

            get { return GetProperty<string>(FileTypeCodeProperty, _fileTypeCode); }

            set { SetProperty<string>(FileTypeCodeProperty, ref _fileTypeCode, value); base.BusinessRules.CheckRules(FileTypeCodeProperty); }

        }

 

        public string Description

        {

            get { return GetProperty<string>(DescriptionProperty, _description); }

            set { SetProperty<string>(DescriptionProperty, ref _description,  value); }

        }

 

        public string ClassCode

        {

            get { return GetProperty<string>(ClassCodeProperty, _classCode); }

            set { SetProperty<string>(ClassCodeProperty, ref _classCode, value); }

        }

        protected override void AddBusinessRules()

        {

            base.AddBusinessRules();

            BusinessRules.AddRule(new MyRequired() { PrimaryProperty = FileTypeCodeProperty } );

            BusinessRules.AddRule(new Csla.Rules.CommonRules.MaxLength(FileTypeCodeProperty, 50));

            BusinessRules.AddRule(new Csla.Rules.CommonRules.MaxLength(DescriptionProperty, 50));

            BusinessRules.AddRule(new Csla.Rules.CommonRules.MaxLength(ClassCodeProperty, 20));

        }

 

    class MyRequired : Csla.Rules.BusinessRule

    {

        protected override void Execute(RuleContext context)

        {

            FileType target = (FileType)context.Target;

 

            if (target.FileTypeCode == string.Empty || target.FileTypeCode == null)

                context.AddErrorResult("File Type Code is required.");

        }

    }

RockfordLhotka replied on Tuesday, July 20, 2010

This works:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      var obj = Csla.DataPortal.Create<Test>();
      Console.WriteLine(obj.IsValid.ToString());
      Console.WriteLine(obj.BrokenRulesCollection[0].Description);
      Console.ReadLine();
    }
  }

  [Serializable]
  public class Test : BusinessBase<Test>
  {
    public static PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
    public string Name
    {
      get { return GetProperty(NameProperty); }
      set { SetProperty(NameProperty, value); }
    }

    protected override void AddBusinessRules()
    {
      base.AddBusinessRules();
      BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(NameProperty));
    }
  }
}

LarzStarz replied on Wednesday, July 21, 2010

I am able to demonstrate the issue I'm having here.  I used your example and it worked fine.  However, when I try it using the way I was declaring my class property with private member field, it doesn't work.  Try this and you'll see what I mean.  The initial state of the object is correct, but when I set the Name property to a non empty string, the object of type Test2 is correctly marked as IsValid, but the object of type Test is not.  Please give this a try and let me know what I might be doing wrong.  Thanks.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Csla;

 

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            Console.WriteLine("Test Class:");

            Test obj = Csla.DataPortal.Create<Test>();

            Console.WriteLine(obj.IsValid.ToString());

            Console.WriteLine(obj.BrokenRulesCollection[0].Description);

            obj.Name = "Name";

            Console.WriteLine(obj.IsValid.ToString());

            obj.Name = "";

            Console.WriteLine(obj.IsValid.ToString());

            Console.WriteLine(obj.BrokenRulesCollection[0].Description);

 

            Console.WriteLine("Test2 Class:");

            Test2 obj2 = Csla.DataPortal.Create<Test2>();

            Console.WriteLine(obj2.IsValid.ToString());

            Console.WriteLine(obj2.BrokenRulesCollection[0].Description);

            obj2.Name = "Name";

            Console.WriteLine(obj2.IsValid.ToString());

            obj2.Name = "";

            Console.WriteLine(obj2.IsValid.ToString());

            Console.WriteLine(obj2.BrokenRulesCollection[0].Description);

 

            Console.ReadLine();

        }

    }

 

    [Serializable]

    public class Test : BusinessBase<Test>

    {

        private static PropertyInfo<string> NameProperty = RegisterProperty<string>(typeof(Test), new PropertyInfo<string>("Name"));

        private string _name = NameProperty.DefaultValue;

 

        public string Name

        {

            get { return GetProperty(NameProperty, _name); }

            set { SetProperty(NameProperty, ref _name, value); }

        }

 

        protected override void AddBusinessRules()

        {

            base.AddBusinessRules();

            BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(NameProperty));

        }

    }

 

    [Serializable]

    public class Test2 : BusinessBase<Test2>

    {

        public static PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);

        public string Name

        {

            get { return GetProperty(NameProperty); }

            set { SetProperty(NameProperty, value); }

        }

 

        protected override void AddBusinessRules()

        {

            base.AddBusinessRules();

            BusinessRules.AddRule(new Csla.Rules.CommonRules.Required(NameProperty));

        }

    }

}

 

RockfordLhotka replied on Wednesday, July 21, 2010

LarzStarz

However, when I try it using the way I was declaring my class property with private member field, it doesn't work.  

That is expected. I didn't notice you were using private backing fields.

If you use private backing fields you need to add a relationship type to your RegisterProperty() call to mark the property as a PrivateField type. That tells CSLA to use a different technique to get the property value - specifically it will use reflection to invoke the property getter instead of using the field manager to get the value (because of course the field manager doesn't have the value).

LarzStarz replied on Wednesday, July 21, 2010

Thanks, Rocky.  Using the PrivateField relationship type worked fine.  Thanks for the quick feedback.

Copyright (c) Marimer LLC