executing factory method for default value of RegisterProperty

executing factory method for default value of RegisterProperty

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


am_fusion posted on Tuesday, December 22, 2009

We ran into an issue in a class that wanted to execute a factory method to specify a default value for RegisterProperty:

private static PropertyInfo<ChildBusinessBaseClass> ChildInstanceProperty = RegisterProperty<ChildBusinessBaseClass>(c=>c.ChildInstance, "instance of child with default value retrieved from database", ChildBusinessBaseClass.NewInstance())

The situation here is that when the child class is retrieving it's data it is expecting that the ApplicationContext.User is our custom principal\identity.  This works fine except when the parent object is being saved. 

The order of operations is:

  1. WcfPortal.Update(UpdateRequest request) is called 
  2. WcfPortal.Update eventually calls GetCriteria(request.ObjectData)
    1. GetCriteria eventually calls MobileFormater.Deserialize(XmlReader reader)
      1. MobileFormater.Deserialze makes a call to Activator.CreateInstance(type, true)
        • at this point type = the BO that is being updated... I.E. our parent object
        • so when the object is instanced, our static register property method is called which wants to execute a factory method to retrieve the default value (which requires ApplicationContext.User to be correctly set.
        • NOTE: the context has not yet been deserilaized and set so ApplicationContext.User is still generic principal.
  3. SetContext is called when processing returns to WcfPortal.Update

we have worked around for this issue by only populating default values in the DataPortal_Create method... I actually question the wisdom of using factory methods to retrieve default values for the RegisterProperty methods since a developer may not realize that data access is occurring (as it is easy to overlook the default value specification in the RegisterProperty method).

I guess my question is, would it be worthwhile to make sure that SetContext (I.E. the Principal\Identity get deserialized and set) happens before the MobileFormatter creates the instance of the object?

Considering that any logic running in the constructor should probably be running with the expected Principal set I would lean towards yes; we should make sure that SetContext is happening before any of our (subclass code) is run.

 

ajj3085 replied on Tuesday, December 22, 2009

You wouldn't be able to get this to work unless the RegisterProperty took a delegate in place of a default value.  The register property call you have setup will result in the same child instance used for all root instances... which isn't supported by Csla (one child with multiple parents).

I'm not sure I'm clear on the problem you're encountering though.  If you need to get a default from the database, that's exactly what DataPortal_Create is for.  So your workaround is actually what you should be doing for a default value which comes from the database.

There was some discussion on whether or not RegisterProperty should throw an exception if the defaultValue parameter isn't a value type.  Maybe this should happen, or RegisterProperty should take a delegate to call when a default value is needed.

am_fusion replied on Tuesday, December 22, 2009

Hmmm... yes, what you say makes sense.  In our case it would be acceptable for the same instance to be used since it is a readonlybase (and should not change durring its lifetime)... however, it feels 'bad' to do since it is not obvious to the developer that the same instance will be used (I know it didn't occur to me when I first looked at it... though it should have heh heh).  And 'one day' someone may decide that it makes sense to manipulate the state of the object without realizing that the same object could potentialy be referenced from another parent.

That being said, it still seems to me that we should be setting the context before anything else occurs.  A good example is if someone were to put logic in the ctor, that logic would be running with a generic principle\identity attached to the thread... this seems bad to me... then again, I would hesitate to put logic in the ctor anyway (it seems to me that the factory methods would be the best place to put that kind of logic).

Point being, we would want *any* code that we write to be running under the expected principal\identity. 

Regarding RegisterProperty throwing an exception on non value types: I would vote for the delegate, but that gets us back to the previous sentance.  SetContext must happen before GetCriteria so that the delegate would be running under the expected principal\identity. EDIT:  after thinking about this more, that is not an issue since the delegate itself will most likely be invoked after SetContext has occured...

ajj3085 replied on Tuesday, December 22, 2009

Well for now, initializing a child reference via RegisterProperty is not supported, and the proper way to do this would be using the DataPortal_Create.

am_fusion replied on Wednesday, December 23, 2009

This is the recomendation we have given to our developers.  (Always use Dataportal_Create to initialize default values)...  As much as I like the feature of having default value specification on the property meta data, for consistancy we will not be using this feature.

Also, we are raising an awareness that any code that executes when the object is instantiated (I.E. ctor or static calls like RegisterProperty) is not guaranteed to run under the expected principal\identity.

Copyright (c) Marimer LLC