Some doubt of Business Rule

Some doubt of Business Rule

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


Sam_Lin posted on Tuesday, May 28, 2013

Dear All:

           I am a junior in CSLA framework. There's a doubt when i reading the "Using CSLA 4" about the business rule: It says "the target property can only be used on the ui thread and any use of this property on a background thread will lead to cross-thread exceptions or nasty threading bugs"

My doubt:

1. this rule is only suitable for async rule or both sync/async rule?

2.if i don't have the ui thread, and it just a service to expose interface to others,in this case, can i use the target ?

3. Dose everyone could illustrate some detail example to explain what problem will occur when use target in background thread?

Thanks.

JonnyBee replied on Tuesday, May 28, 2013

Hi,

Typically - rules is triggered by a user editing a property in the UI. So it is likely that the rules Execute method will be called on the UI thread on an object that is databound. You will get cross thread exception when you try to set properties on an object in a background thread (actually when the OnPropertyChanged event is called and there is eventlisteners on another thread).

When rules have IsAsync = true this tells the RuleEngine to not call context.Complete automatically. It is then your code´s responsibility to call context.Complete when the async operation is completed. 

When you use ASP.NET or WCF you will typically load data into the object and call BusinessRules.CheckRules(). This is more efficient than calling rules on every property setter.

So your rules may be sync or async. Async rules may only get the contextTarget when ProvideTargetWhenAsync = true and you explicitly take reposibility for interaction with context.Target. 

What is the rule expected to do?

Sam_Lin replied on Wednesday, May 29, 2013

Thanks, JonnyBee

      i think i can descripe this rule as below:

             Target(BusinessBase) is not thread safety, There will be a problem when multi-thread access the target and its properties

            That's mean when i guarantee the target is thread safety, there won't be a problem.

Right?

JonnyBee replied on Thursday, May 30, 2013

Hio,

Most of .NET framework is NOT thread safe so I'm afraid I do not quite understand how you can make target thread safe.

For CSLA part it is mostly UI Events that causes problems when they occur from a thred that is NOT the UI Thread.
This will cause cross-thread exception to be thrown.

I stil do not understand what the purpose of the rule is.

Sam_Lin replied on Thursday, May 30, 2013

OK, don't worry about that. It just the way of my understanding(i don't want to bring the UI/Background thread to it) . Maybe it's very hard to make the target thread safe.

So, my understanding is right?

 

Thanks Jonney.

thaehn replied on Thursday, May 30, 2013

Here is an example of running a rule both on the current UI thread and using an async call on another thread.  The UI thread just checks to see that the name isn't empty.  Then a command is called to check the database that the name is unique and doesn't duplicate another name in the database:

    using Csla.Core;

    using Csla.Rules;

 

    /// <summary>

    /// The group validate name rule.

    /// </summary>

    public class GroupGroupValidateNameRule : BusinessRule

    {

        public GroupGroupValidateNameRule(IPropertyInfo primaryProperty)

            : base(primaryProperty)

        {

            ProvideTargetWhenAsync = true;

            IsAsync = true;

 

            AffectedProperties.Add(GroupGroupItem.NameProperty);

        }

       

        protected override void Execute(RuleContext context)

        {

            GroupGroupItem target = (GroupGroupItem)context.Target;

            if (target.Name == string.Empty)

            {

                context.AddErrorResult("Group Name Cannot Be Empty.");

                context.Complete();

            }

            else

            {

                // Check for duplicated name

                GroupGroupValidateNameCommand.BeginExecute(target.Name, target.Id, (o, e) =>

                {

                    if (e.Error != null)

                    {

                        throw e.Error;

                    }

                    else

                    {

                        if (e.Object.Duplicate)

                        {

                            context.AddErrorResult("Group Name Is Already In Use.  Please Choose Another Name.");

                            context.Complete();

                        }

                        else

                        {

                            context.AddSuccessResult(true);

                            context.Complete();

                        }

                    }

                });               

            }

        }

    }

 

Hope this will help find what you are looking for.

Todd

JonnyBee replied on Thursday, May 30, 2013

Hi Todd, 

Your rule is not correct. You MUST make sure to always call context.Complete in the callback - or else the object/property will remain "busy" and you will not get the ValidationCompleted event. 

So - you should not throw the exception - rather add the error message as AddErrorMessage and call context.Complete().

thaehn replied on Thursday, May 30, 2013

Thanks, Jonny.  One of the boiler-plate cut and paste things... Here is a new copy:

    using Csla.Core;

    using Csla.Rules;

 

    /// <summary>

    /// The group group validate name rule.

    /// </summary>

    public class GroupGroupValidateNameRule : BusinessRule

    {

        public GroupGroupValidateNameRule(IPropertyInfo primaryProperty)

            : base(primaryProperty)

        {

            ProvideTargetWhenAsync = true;

            IsAsync = true;

 

            AffectedProperties.Add(GroupGroupItem.NameProperty);

        }

       

        protected override void Execute(RuleContext context)

        {

            GroupGroupItem target = (GroupGroupItem)context.Target;

            if (target.Name == string.Empty)

            {

                context.AddErrorResult("Group Name Cannot Be Empty.");

                context.Complete();

            }

            else

            {

                // Check for duplicated name

                GroupGroupValidateNameCommand.BeginExecute(target.Name, target.Id, (o, e) =>

                {

                    if (e.Error != null)

                    {

                        context.AddErrorResult(e.Error.Message);

                        context.Complete();

                    }

                    else

                    {

                        if (e.Object.Duplicate)

                        {

                            context.AddErrorResult("Group Name Is Already In Use.  Please Choose Another Name.");

                            context.Complete();

                        }

                        else

                        {

                            context.AddSuccessResult(true);

                            context.Complete();

                        }

                    }

                });               

            }

        }

    }

 

Todd

Sam_Lin replied on Thursday, May 30, 2013

If i change properties of target in the call back method, It will be a problem? If there's no other thread access the target such as "OnPropertiesChanged",it will still be a problem ?

JonnyBee replied on Thursday, May 30, 2013

Hi

My general recommendation is to NEVER set an objects properties directly by the property setter in a rule Execute method. This will trigger the rule engine to run from within another rule execute method and may cause infinite loops. 

In order to update property values on an object you may use 

and make sure that all properties to be updated is added to AffectedProperties list in the constructor method of the rule.

The rule engine will then run rules for the affected properties and call OnPropertyChanged for you. 

Sam_Lin replied on Thursday, May 30, 2013

Thanks.

thaehn replied on Friday, May 31, 2013

Sam Lin,

Here is an example of setting properties in a rule:

    class DataEntrySetIssueItemSlaRule : BusinessRule

    {

        public DataEntrySetIssueItemSlaRule(IPropertyInfo primaryProperty)

            : base(primaryProperty)

        {

            InputProperties = new List<IPropertyInfo> { PrimaryProperty };

 

            AffectedProperties.Add(DataEntryIssueItem.SlaDueByProperty);

            AffectedProperties.Add(DataEntryIssueItem.ShowSlaProperty);

        }

 

        protected override void Execute(RuleContext context)

        {

 

            var target = (DataEntryIssueItem)context.Target;

            if (target.CustomerStdHrs > TimeSpan.Zero || target.CustomerCrtHrs > TimeSpan.Zero)

            {

                DateTime? slaDate = null;

 

                // Critical Part and Critical Problem gets Critical Hrs

                if (target.PartCritical && target.Problem.Critical && target.CustomerCrtHrs > TimeSpan.Zero)

                {

                    // Calculate SLA date

                    slaDate = target.CalendarEventList.CalculateDate(target.Added, target.CustomerCrtHrs);

                }

                else

                {

                    if (target.CustomerStdHrs > TimeSpan.Zero)

                    {

                        slaDate = target.CalendarEventList.CalculateDate(target.Added, target.CustomerStdHrs);

                    }

                }

                context.AddOutValue(DataEntryIssueItem.SlaDueByProperty, slaDate);

                context.AddOutValue(DataEntryIssueItem.ShowSlaProperty, slaDate != null);

                context.Complete();

            }

        }

    }

 

Todd

Copyright (c) Marimer LLC