CSLA 4 - Business Rules: context.Target property

CSLA 4 - Business Rules: context.Target property

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


Jaans posted on Sunday, January 09, 2011

What I gather from the original blog posts regarding the new CSLA 4 business rules system:

It seems that the Target property cannot be guaranteed? I understand that by default, the target property isn't available for ASYNC rules, and that it would require the setting ProvideTargetWhenAsync = true.

Not sure if this is still the case since there has been plenty of work towards improvements done since.What I'm after is more determinism - under what circumstances will the Target property not be populated and left NULL?

A practical point is that I need access to the IsDirty / IsNew properties of the BO inside the rule's Execute method. I cannot supply these as InputProperties because they are not PropertyInfo objects and so I can only get them from the BO at the Target property. That's fine, but I need to make sure that I will have a value for Target for both Sync and Async scenarios.

Are there any gotcha's I should be aware of. Eg. PerType / PerInstance; Async vs. Sync; ValidationRules vs. BusinessRules; etc.

Thanks in advance!
Jaans

JonnyBee replied on Sunday, January 09, 2011

Hi Jaans,

The Context.Target property is by default null if IsAsync == true and ProvideTargetWhenAsync == false (the default value). So you must ProvideTargetWhenAsync to true in order to get the target object in an async rule.

Csla4 Rules system is all PerType rules. PerInstance rules is not supported anymore.

 

 

 

Jaans replied on Sunday, January 09, 2011

Thanks Jonny

Are you confirming then that provided I set ProvideTargetWhenAsync = true for async implementations, the Target property will be populated?

JonnyBee replied on Monday, January 10, 2011

Yes, it's easy to check in the Csla4 source code (Rules\BusinessRules.cs) :

        context.Rule = rule;
        if (!rule.IsAsync || rule.ProvideTargetWhenAsync)
          context.Target = _target;

RockfordLhotka replied on Monday, January 10, 2011

The reason you must request that the Target value be populated is that any use of the Target property by a background thread will almost certainly cause problems. Some problems might be easy to find (cross-threading exceptions), but others could be subtle race conditions that are very hard to find/fix.

So I default to not making Target available. My assumption is that if you request access to Target that you will know enough to absolutely never, ever, ever use Target in any actual async code.

In short, when someone does make this mistake (and I'm sure they will), I want to be able to say "I told you so" and leave it at that :)

Jaans replied on Monday, January 10, 2011

Thanks for that Rocky - makes sense.

Given that, do you have any suggestions to accomplish the following scenarios using the "InputProperties + AddOutPutValue" pattern?

(Some of it relates to this post: http://forums.lhotka.net/forums/t/9927.aspx)

Scenario 1: Rule on Parent property should mutate/affect child instances in child collection

Here I can see adding the ChildCollection (Editable Child Collection) Property to AffectedProperties in the constructor of the rule, but I'm not sure about the execute method. It doesn't make sense to call context.AddOutValue for the same Child Collection Property because I have not changed the property, nor the collection itself - but what I have changed was properties on the child business objects in the collection. I guess I could have a scenario where I add/remove items in the collection also.

Would this be possible to do without accessing Target and also in an Async scenario?

Scenario 2: Mutating other properties, without causing rules for those properties to trigger.

As Jonny Bekkum pointed out I could use LoadProperty and specify Target to achieve this, which is great for sync usages. Again, how could I do this in an async fashion, safely. Basically, I'm looking for an context.AddOutValue that has an parameter that allows me to suppress rule checking like I would when using LoadProperty / BypassPropertyChecks

Scenario 3: Accessing BO properties that are not IPropertyInfo implementations

So here, I'm asking using the how I could use the "InputProperties + AddOutputValue" pattern for async.

I guess I could relatively safely "read (only)" the IsDirty / IsNew / IsValid / etc. properties from Target. Would the "MarkAsXXXX" methods be safe to call on target?

I guess the simplest workaround for the above kind of rules is to stick to synchronous usage and accessing target.

Thanks for helping!
Jaans

JonnyBee replied on Tuesday, January 11, 2011

Hi Jaans,

Scenario 1: I would not use context.AddOutValue on this one. You should process the child list and do the updates you need syncronously.

Scenario 2: What type of properties are we talking about here? 

If this is a certain type of property it could also be handled (to a certain degree) in how you define the property, (ie: property stereotypes) ex:

The main issue here is in what code is executed asyncronously. In the async callback you can safely interact with the Target object - but you should not set any values/properties in the async code.

Scenario 3: You must get access to context.Target to get properties that are not IPropertyInfo implementations.

Why would create rules that should alter the state management of the BusinessObject, like MarkXYZ?
Are you creating rules that is actually being a sort of DataAccess?

I would not recommend to create rules that call MarkXYZ.....   These methods are protected (only accessible in class and descendants) so you would need to create overrides that are public or internal to gain access to them and could potentially create a mess  in object state.

Using context.AddOutValues:
The purpose of context.AddOutValues is to make a more generic way of updateing other properties in your BO from a rule. The process is as follows:

1. After rule is executed (sync: rule.Execute() is finished, async: context.Complete() is called)
2. Call non-generic LoadProperty to load values into BO
3. Check rules for all properties that was changed (AffectedProperties)
4. If (ApplicationContext.PropertyChangedMode = PropertyChangedModes.Windows
          raise OnPropertyChanged for "PrimaryProperty")
    else
          raise OnPropertyChanged for all affectedProperties (including PrimaryProperty).

So the intending process is to make sure rules are run for all properties that are changed.

 

Copyright (c) Marimer LLC