Improvements for private backing fields

Improvements for private backing fields

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


xal posted on Wednesday, December 07, 2011

I created the attached class as a proof of concept. It's use is more specific for private fields but could be used for custom properties (or calculated properties as well).

For my tests I just edited one of the overloads for RegisterProperty like so:

    protected static PropertyInfo<P> RegisterProperty<P>(Expression<Func<T, P>> propertyLambdaExpression, RelationshipTypes relationship)
    {
      return RegisterProperty(new ExtendedPropertyInfo<T, P>(propertyLambdaExpression, relationship));
    }

I then added this to ReadProperty<P>:

      if (((propertyInfo.RelationshipType & RelationshipTypes.PrivateField) == RelationshipTypes.PrivateField))
      {
        var extPI = propertyInfo as Csla.IExtendedPropertInfo<P>;
        if (extPI != null)
          using (BypassPropertyChecks)
          {
            return extPI.GetProperty(this);
          }
      }

And similar for LoadProperty<P>

        if (((propertyInfo.RelationshipType & RelationshipTypes.PrivateField) == RelationshipTypes.PrivateField))
        {
          var extPI = propertyInfo as Csla.IExtendedPropertInfo<P>;
          if (extPI != null)
            using (BypassPropertyChecks)
            {
              extPI.SetProperty(this,newValue);
              return;
            }
        }

The benefits are obvious, being able to interact with these property types through ReadProperty and WriteProperty without the big performance penalties of reflection is very good.

Currently the generic versions of Read/Load Property don't even support interaction with properties that have private backing fields. The non generic versions do, but they use reflection and their performance is really slow.

Most of the time, the properties are being registered using the lambda variant of RegisterProperty, so in this scenario, we could easily load this "extended" version of property info and benefit from the performance improvements. Even if the lambda variant wasn't used, the same behaviour could be achieved as well, with a little more effort. Of course I'd be glad to extend this even further, but I just wanted to throw this proof of concept out there first and see what other people think.

 

Cheers,

Andrés

JonnyBee replied on Thursday, December 08, 2011

Hi Andres,

I'd rather remove the need for private backing fields than add ExtendedPropertyInfo to CSLA. Just my personal opinion.

In an ideal situation we would only ever use managed properties in CSLA that would automatically be serialized with MobileFormatter/CslaFormatter.

For now -  we need private backing fields to support [NotUndoable] and [NonSerialized] and developers must add extra code in OnSerialize/OnDeserialize for private fields when using MobileFormatter (or the CslaFormatter in next version).

See: http://www.lhotka.net/cslabugs/edit_bug.aspx?id=30 

The RegisterProperty methods that do not take a PropertyInfo parameter should always use the PropertyInfoFactory approach. This is an extension point for developers and is used f.ex in CustomFieldData (http://jasonbock.net/jb/Default.aspx?blog=entry.9cc70d85bef34e2b9a683ba82615f8a3)

xal replied on Thursday, December 08, 2011

Hi Jonny,

Yes, I realize that you're supposed to use PropertyInfoFactory, as I said, this was done just for testing purposes.

The extended class is a proof of concept, this functionality could be implemented directly in the current propertyinfo object without needing the extra extendedpropertyinfo object.

It doesn't apply only to private fields. Calculated properties would benefit from this as well. It amazes me that they were never factored in. I'll create a sample project to demonstrate why calculated properties are needed and extremely important, but that's for another topic.

 

Andres

 

PS: the original version of the setter did not work in windows phone. This version does:


      public ExtendedPropertyInfo(Expression<Func<C, T>> prop, RelationshipTypes relationship)
        : this(Csla.Reflection.Reflect<C>.GetProperty(prop), relationship)
      {
        _getter = prop.Compile();

        if (_propertyInfo.CanWrite)
        {
          var mi = _propertyInfo.GetSetMethod();
          var obj = Expression.Parameter(typeof(C), "o");
          var param = Expression.Parameter(typeof(T), "value");
          var call = Expression.Call(obj, mi, param);
          var set = Expression.Lambda<Action<C, T>>(
                  call,
                  obj, param);
          _setter = set.Compile();
        }
      }

RockfordLhotka replied on Thursday, December 08, 2011

In 4.5 I am considering abandoning Microsoft's serializers completely. If Sergey's new serialization enhancements in 4.3 work out as well as we hope, our serializer will be more efficient, and we have direct control over it.

That'll make it practical to implement "non-serialized" as a concept for managed backing fields with no overhead.

The problem with doing it currently, is that (to support the binaryformatter) we'd need to maintain two separate lists of field data - one serialized, the other not - in memory for each business object. Certainly something we could do, but also something that incurs overhead.

If we directly control serialization, we can make this work without multiple lists in memory and that's better.

A lot depends on just how well the new serialization features work out in practice. Look for an alpha of 4.3 very soon so we can get some feedback on the changes.

Copyright (c) Marimer LLC