Private backing fields and non generic LoadProperty

Private backing fields and non generic LoadProperty

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


zep2j posted on Monday, February 07, 2011

Hi, I am new to CSLA and I am trying to use properties with private backing fields in business rules.

I have registered my properties with RelationshipTypes.PrivateField.  When I use the non generic LoadProperty method with these private backing fields I get an exception stating "Attempt to read/load private field property in managed properties".

This seems to be a contradiction to what has been stated in the CSLA 4 documentation and previously stated on this forum:

"The PrivateField flag is intended to tell CSLA that you are using a private field. So you do need to use the private field syntax in your property declaration - and now you can tell CSLA that this is what you are doing.

That is important, because it means that the non-generic ReadProperty and LoadProperty methods then know that they should use BypassPropertyChecks and reflection to interact with the property, rather than using the field manager."

I have looked at the CSLA code and the non generic LoadProperty method calls the generic LoadProperty method using reflection which subsequently calls FieldManager.GetFieldData which throws an exception if the property is registered with PrivateField.

This becomes an issue when trying to use context.AddOutValue in a business rule as LoadProperty is called to set the property value once the rule has completed.

From looking at the CSLA code ReadProperty seems to behave correctly with private backing fields.

Is this a bug or am I doing something completely wrong?

RockfordLhotka replied on Monday, February 07, 2011

This appears to be a bug, I'll add it to the bug list.

RockfordLhotka replied on Monday, February 07, 2011

Hmm, digging into this a little further, I'm not sure it is a bug. I think this is by design to the best of my recollection.

And here's why:

Exactly how would something like LoadProperty(IPropertyInfo, object) ever figure out which backing field to use?

At one point in the evolution of the code we did have LoadProperty call the property setter via reflection, but that's not a workable solution because the setter could be non-public (it always is in a read-only object), so it is not accessible. And this is why the non-generic LoadProperty now invokes the generic LoadProperty.

But as you note, this only works with managed property values.

So we're back to this core issue - .NET has no idea how to map any given property to a specific private field. There's no mechanism for this within .NET anywhere - other than the code you write in your getter and setter, and that code can't be assumed to be accessible.

I suppose we could do something interesting, such as:

  1. In the generic LoadProperty detect that this is a private field property
  2. In that case cast the object to some IHavePrivateFields interface
  3. Call a SetFieldValue method from IHavePrivateFields like: SetFieldValue(IPropertyInfo, newValue)
  4. You would manually implement IHavePrivateFields in your business class, and in the SetFieldValue method you would map IPropertyValue values to the appropriate backing field values

We did implement something like this at one point in the evolution of CSLA 4, but the functionality was removed because it leads to code that is hard to maintain. Specifically, that SetFieldValue method is basically a big nested if..then or switch block that is easy to get wrong or forget to update as you add/remove properties and fields.

Ultimately we decided that people using private fields can come up with their own mechanism to interact with their fields. The primary reason for using private fields is performance, in which case having us do all sorts of fancy reflection and so forth is entirely counter-productive and people should have used managed fields to start with :)

To retain the perf benefits of using private fields, I think it is best if people come up with their own light-weight access schemes that avoid reflection or dynamic method/property invocation.

JonnyBee replied on Monday, February 07, 2011

Actually, what you should do is override the Non-generic LoadProperty to set your private fields, like this:

    protected override void LoadProperty(IPropertyInfo propertyInfo, object newValue)
    {
// Set private fields
      if (propertyInfo.Name == NameProperty.Name)
      {
        _name = (string) newValue; 
      }
// call base implementation to set managed properties
      else
      {
        base.LoadProperty(propertyInfo, newValue);
      }      
    }

For the performance and access reasons given in Rocky's post.

RockfordLhotka replied on Monday, February 07, 2011

Ahh, good job Jonny - I thought we had an answer for this, but it totally slipped my mind...

zep2j replied on Monday, February 07, 2011

Yeah, I am already overriding LoadProperty.

I just thought I would raise it because the documentation stated something different and the ActionExtenderSample project has an override of LoadProperty commented out which I assumed was implying that you do not need to do that anymore with CSLA 4.

Thanks.

Copyright (c) Marimer LLC