MarimerLLC / cslaforum

Discussion forum for CSLA .NET
https://cslanet.com
Other
31 stars 6 forks source link

Metadata in business rules? #236

Open ajj7060 opened 8 years ago

ajj7060 commented 8 years ago

This came up today and I realized I'm not sure how to accomplish this. We ended up not needing it, but I would like to ask for future reference.

Sometimes in a business rule I'll want to take into account the instances metadata, in the case today it would be IsNew, as the rule should only fire for old objects. Is there a way to access this meta data from the RuleContext only? I tend to prefer avoiding getting at the RuleContext.Target as it makes unit testing the rules a bit easier, and in this case the rule is a good candidate for an async rule where accessing the target presents problems of its own.

If there's no way to do this currently, could we add an issue to consider it for a future time?

rockfordlhotka commented 8 years ago

I've never thought to try this, but you should be able to add any managed property (including the IsNew property) to the InputProperties list for a rule and have access via that mechanism.

ajj7060 commented 8 years ago

Maybe I'm missing something, but I don't see any PropertyInfo fields for IsDirty, IsNew, etc. This post (http://forums.lhotka.net/forums/p/4808/23427.aspx) from the old forum suggests they were there at one point but removed before that version of the framework came out of beta due to concerns of outside code modifying IsNew.

rockfordlhotka commented 8 years ago

Yes, you are correct, forgot about that.

In that case please do write up an issue in the csla repo - preferably if you have some suggestions on how you think the experience should look from inside the rule.

jonnybee commented 8 years ago

You could consider to cast RuleContext.Target to ITrackStatus and access the metadata properties through this interface.

Another possible solution is to add PropertyInfo fields and mark as PrivateField. Then you must also override ReadProperty method to supply the value from the private field/property. Csla internally has a separate method MetaDataHasChanged:

protected virtual void MetaPropertyHasChanged(string name)
{
  if (ApplicationContext.PropertyChangedMode != ApplicationContext.PropertyChangedModes.Windows)
    OnPropertyChanged(name);
}

And this will make sure that NO business rules is triggered when metadata properties is changed (and event is only called when PropertyChangedMode != Windows).

But this could also lead to awkward situations where developers create:

ajj7060 commented 8 years ago

Well I'm trying to avoid using .Target on the context as the rule in my use case would be Async, and interacting with the BO while bound to the UI can cause issues.

I think the ReadProperty suggestion has the same issue; at the very least async rules could get differing values for IsDirty, or at least that seems possible to me. That's why I though the values should be added to the RuleContext and handed off, much the same way InputProperties are copied to the context, not read live from the BO.

It would be nice if all the data from ITrackStatus was available (e.g. context.TargetIsNew, context.TargetIsSelfDirty), although I'm not sure how Dirty/Valid would work. IIRC, async rules all get the same copy of the data when the rule is executed, but they don't see each others changes. New, Deleted and Child don't change while rules would be running so copying those values seems fine.

If that sounds reasonable, I can add a ticket to the csla backlog.

jonnybee commented 8 years ago

If you decide to add PropertyInfo's for IsDirty then the only way to make it work with InputProperties is to add the ReadProperty overload so that the RuleEngine is able to set the correct value into context.InputPropertyValues!

IsDirty/IsValid doesn't make sense to me - at least in terms of how the rule should execute.

TargetIsNew and TargetIsSelfDirty could be added to RuleContext (and also make sure to propagate down in GetChainedContext).

You could however achieve the same effect by using RuleSet (different ruleset for "new" and "existing" objects and set the ruleset to use in XYZ_Create/XYZ_Fetch/XYZ_Insert).

I would also point out that when you inherit from PropertyRule base class you can also instruct rule engine of when the rule is allowed to be executed:

When all 3 is false the rule would only be allowed to run from PropertyHasChanged (ie: the primary property value has changed and by definition the object is Dirty).

I believe this is an edge case - usually rules should not care whether the object is dirty or new.