Implicit Value Transformation Examples

With implicit value transformations you let Obtics build the transformation pipeline for you. This method is much more convenient than creating the pipeline by hand. The resulting pipline though strives for maximum change awareness. This means it listens for possible changes on all properties and that in turn will create some overhead. Creating the pipeline generator will also generate overhead but in most cases this can be neutralised.

Class Person in these examples is an observable class. That means an instance of this class sends change notifications whenever a property changes value. See http://www.codeplex.com/Obtics/SourceControl/FileView.aspx?itemId=209671&changeSetId=14599 for a possible implementation.

using Obtics.Values;

class Test
{
        Person _Person = new Person("Glenn","Miller");

        Person Person
        { get { return _Person; } }

        public IValueProvider<int> PersonFullNameLength
        {
            get 
            {
                return 
                    ExpressionObserver.Execute(this, t => t.Person.FirstName.Length + t.Person.LastName.Length + 1);

                //the below line is even simpler but because the lambda expression depends on the external 'this' variable the
                //expression is re-compiled for every Test class instance. Better use lambda's without external variables.
                //return ExpressionObserver.Execute(() => Person.FirstName.Length + Person.LastName.Length + 1);
            }
        }
}


In case the PersonFullNameLength property getter gets called more often it will be worth the effort to cache the compiled LambdaExpression. A caching mechanism already prevents the LambdaExpression from being recompiled (as long as it doesn't contain any free variables a.k. is 'pseudo-pure') but the entire expression tree still needs to be built and compared with expression trees already in the cache. Thuis building and comparing can be avoided if the compiled expression is saved in a static field.

using Obtics.Values;

class Test
{
        Person _Person = new Person("Glenn","Miller");

        Person Person
        { get { return _Person; } }

        static readonly Func<Person,IValueProvider<int>> _PersonFullNameLengthF = 
            ExpressionObserver.Compile( (Test t) => t.Person.FirstName.Length + t.Person.LastName.Length + 1);

        public IValueProvider<int> PersonFullNameLength
        { get { return _PersonFullNameLengthF(this); } }
}

Code snippets for this instance property pattern can be found here: http://obtics.codeplex.com/SourceControl/changeset/view/19469#368332.

In case instances of class Test are rare and the getter of the PersonFullNameLength property gets called often then it may be advantageous to cache the resulting IValueProvider in a cache. In this example a LocalPipelineCache struct is used to store the IValueProvider localy. See Pipeline Caches for examples of caches.

using Obtics.Values;

class Test
{
        Person _Person = new Person("Glenn","Miller");

        Person Person
        { get { return _Person; } }

        static readonly Func<Person,IValueProvider<int>> _PersonFullNameLengthF = 
            ExpressionObserver.Compile( (Test t) => t.Person.FirstName.Length + t.Person.LastName.Length + 1);

        LocalPipelineCache<IValueProvider<int>> _PersonFullNameLength;

        public IValueProvider<int> PersonFullNameLength
        {
            get 
            {
                return _PersonFullNameLength.Get(this, _PersonFullNameLengthF) ;
            }
        }
}


See also: Transformation Examples, Pipeline Caches

Last edited May 7, 2009 at 3:26 PM by Throb, version 5

Comments

joshmouch Sep 17, 2009 at 10:02 PM 
Let me change that last comment. What I meant to say was that it occurs when I pass a reference to myself as the first parameter to the expression. If the expression contains the reference as an "external variable" (making the expression "non-pure") then it is not broken.

joshmouch Sep 17, 2009 at 9:48 PM 
I should also note that the error only occurs when the expression is declared statically. For example, the following does not have the same problem:
<code>

Private ReadOnly WeekTimeSlotsExpression3 As Expressions.Expression(Of Func(Of IEnumerable(Of ScheduleDateRange))) = _
Function() _
From range In Me.VisibleRange.Split(Me.WeekTimeSlotsDuration)

</code>

However, if I read the documentation correctly, that is a "non-pure" expression and will need to be recompiled for every use.

joshmouch Sep 17, 2009 at 9:42 PM 
Okay, I think I figured out specifically what causes the problem. As soon as I cast the Cascade() result to INotifyCollectionChanged then it seems to "freeze" the value (i.e. always returns the same results).

If you change the property in my last comment to the following the both Expressions are broken:
<code>

Public ReadOnly Property WeekTimeSlots() As IEnumerable(Of ScheduleDateRange)
Get
' DEBUG:
Dim a = ExpressionObserver.Execute(Me, WeekTimeSlotsExpression).Cascade()
Dim a1 = DirectCast(a, INotifyCollectionChanged)
Dim b = ExpressionObserver.Execute(Me, WeekTimeSlotsExpression2).Cascade()
Dim b1 = DirectCast(b, INotifyCollectionChanged)

Return ExpressionObserver.Execute(Me, WeekTimeSlotsExpression).Cascade()
End Get
End Property
</code>

joshmouch Sep 17, 2009 at 9:37 PM 
This is odd. I did the following:
I created a second copy of the Expression in question, and performed identical things to both copies (at least in my code), but with one key difference. The first copy has its Cascade() value returned by a property and the second copy doesn't. The second copy has the correct value and the first copy does not.

So, here are my expressions:
<code>
Private Shared ReadOnly WeekTimeSlotsExpression As Expressions.Expression(Of Func(Of WeekCalendarViewModel, IEnumerable(Of ScheduleDateRange))) = _
Function(model As WeekCalendarViewModel) _
From range In model.VisibleRange.Split(model.WeekTimeSlotsDuration)

Private Shared ReadOnly WeekTimeSlotsExpression2 As Expressions.Expression(Of Func(Of WeekCalendarViewModel, IEnumerable(Of ScheduleDateRange))) = _
Function(model As WeekCalendarViewModel) _
From range In model.VisibleRange.Split(model.WeekTimeSlotsDuration)
</code>

And here is the property that uses them:
<code>

Public ReadOnly Property WeekTimeSlots() As IEnumerable(Of ScheduleDateRange)
Get
' DEBUG:
Dim a = ExpressionObserver.Execute(Me, WeekTimeSlotsExpression).Cascade().Count()
Dim b = ExpressionObserver.Execute(Me, WeekTimeSlotsExpression2).Cascade().Count()

Return ExpressionObserver.Execute(Me, WeekTimeSlotsExpression).Cascade()
End Get
End Property
</code>

Note that when I break, it is not in the first instance of this object created. That means I'm using the same Expression instance since it is static.
Here's the immediate window. Note that the first value is wrong, and the second correct:
<code>
?ExpressionObserver.Execute(me,WeekTimeSlotsExpression).Cascade().Count()
0
?ExpressionObserver.Execute(me,WeekTimeSlotsExpression2).Cascade().Count()
7
</code>