Question about business rules

Question about business rules

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


Fel posted on Friday, April 17, 2009

In my CSLA Light application, I have two types of business rules.
Generic rules like required string and custom rules that validate against data in database.
I set up CSLA to validate data when they are fetched by calling Validation.CheckRules method in DataPortal.

Let's assume data in database is invalid.
I was expecting that all fields with business validation will be checked when Validation.CheckRules method is executed.
I found that this apply to generic validate but not to custom.

Should it work this way?
Can I invoke specific custom validation on client side?

JoeFallon1 replied on Friday, April 17, 2009

CheckRules will check all the rules.

You must be doing something incorrectly.

It would help if you could post the appropriate sections of your BO code. AddBusinessRules, DP_Fetch, Factory method call etc.

Are you using inheritance? Any rules in other levels?

Fel replied on Friday, April 17, 2009

       protected override void AddBusinessRules()
        {
            ValidationRules.AddRule(Csla.Validation.CommonRules.StringRequired, new Csla.Validation.RuleArgs(AOProperty));
            ValidationRules.AddRule(Csla.Validation.CommonRules.StringRequired, new Csla.Validation.RuleArgs(AccountNoProperty));
            ValidationRules.AddRule(IsValidPort, new AsyncRuleArgs(PortProperty));
        }
...
      private static void IsValidPort(AsyncValidationRuleContext context)
        {
            CommandCheckPort command = new CommandCheckPort(context.PropertyValues["Port"].ToString());
            DataPortal<CommandCheckPort> dp = new DataPortal<CommandCheckPort>();
            dp.ExecuteCompleted += (o, e) =>
            {
                if (e.Error != null)
                {
                    context.OutArgs.Description = "Error checking for valid port " + e.Error.ToString();
                    context.OutArgs.Severity = RuleSeverity.Error;
                    context.OutArgs.Result = false;
                }
                else
                {
                    if (!e.Object.IsValid)
                    {
                        context.OutArgs.Description = "Invalid port Code";
                        context.OutArgs.Severity = RuleSeverity.Error;
                        context.OutArgs.Result = false;
                    }
                    else
                    {
                        context.OutArgs.Result = true;
                    }
                }
                context.Complete();
            };
            dp.BeginExecute(command);
        }
...
   protected void DataPortal_Fetch(SingleCriteria<Ho, string> criteria)
        {
            using (SqlConnection connection = new SqlConnection(DataConnection.ConnectionString))
            {
                connection.Open();
                using (SqlCommand command = new SqlCommand("C_Ho_Fetch", connection))
                {
                    command.CommandType = System.Data.CommandType.StoredProcedure;
                    command.Parameters.Add(new SqlParameter("@No", criteria.Value));
                    using (Csla.Data.SafeDataReader reader = new Csla.Data.SafeDataReader(command.ExecuteReader()))
                    {
                        if (reader.Read())
                        {
                            LoadProperty<string>(No1Property, reader.GetString("No1"));
                            LoadProperty<string>(AOProperty, reader.GetString("AO"));
                            LoadProperty<bool>(ShowCProperty, ReadProperty(AOProperty) == "O");
                            LoadProperty<string>(InvoiceNoProperty, reader.GetString("InvoiceNo"));
                            LoadProperty<string>(PortProperty, reader.GetString("Port"));
                            LoadProperty<decimal>(PcsProperty, reader.GetDecimal("Pcs"));
                        }
                        reader.NextResult();
                        LoadProperty<CList>(CProperty, CList.GetCList(reader));
                        reader.NextResult();
                        LoadProperty<UList>(UsProperty, UList.GetUList(reader));
                        reader.NextResult();
                        LoadProperty<EList>(EsProperty, EList.GetEList(reader));
                    }
                    LoadProperty(KeyProperty, criteria.Value);
                    if (ReadProperty(AOProperty) == string.Empty) MarkNew();
                    ValidationRules.CheckRules();
                }
                connection.Close();
            }
        }

ajj3085 replied on Friday, April 17, 2009

You probably need to call ValidationRules.CheckRules just before you exit your DataPortal_Fetch (and create, if you have one).

Rules are not run automatically, only in response to property changes or a direct call to check.

Fel replied on Friday, April 17, 2009

I though that I'm doing it. ValidationRules.CheckRules is called just before closing connection to server and exiting DataPortal_Fetch method.

JoeFallon1 replied on Monday, April 20, 2009

It looks like this rule:

private static void IsValidPort(AsyncValidationRuleContext context)

is asynchronous and requires a context which is probably missng while still in the Data Portal.

Joe

Fel replied on Monday, April 20, 2009

Thanks Joe.

Yes, it makes sense.

Then, can I invoke specific custom validation/asynchronous business rule on client side?

If yes, how can I do it?

RockfordLhotka replied on Monday, April 20, 2009

Async business rules have limitations compared to synchronous rules.

Async rules only have access to copies of select property values and can't interact with the actual business object. This is because they are running on a background thread, and interacting with the business object would cause nasty locking issues.

Also, the idea with an async rule is to allow the rule method to make an async data portal call. Other scenarios may work too, but the core reason for creating async rules in CSLA was to allow a rule method to make an async data portal call. And that was motivated by Silverlight, where there's no option but to make async data portal calls.

The specific scenario around which we designed async business rules is this: you have a value that is entered by the user, and you want to tell the user whether it is already in use. This requires a database call, which (in SL) must be async. So that means the rule can't be resolved until an async data portal call has occurred.

If you want a rule to run purely on the client-side, then you'd normally just use a synchronous rule, and it will run where it is invoked (such as on the client).

Copyright (c) Marimer LLC