Custom Mappings

Dec 2, 2009 at 2:00 AM

After using Obtics for several hours, we needed to create a custom mapping. :)

It took several hours to figure out how make the mapping and it's hard to know if we did it properly from an efficiency perspective.  (For example, will the compilation cache work properly?)

For other users of the website, it's worth noting that the MyEnumerable code in Obtics' CustomMapping project has another example that's not on the website.

Here are the methods that we mapped.

 

        [ExpressionObserverMapping(typeof(Observable))]
        public static DecimalMeasure<T> Sum<T>(this IEnumerable<DecimalMeasure<T>> list) where T : Quantity
        {
            return list.Aggregate(new DecimalMeasure<T>(0, Quantity.GetDefaultUnit<T>()), (total, item) => total + item);
        }

        [ExpressionObserverMapping(typeof(Observable))]
        public static DecimalMeasure<T> Sum<TSource, T>(this IEnumerable<TSource> list, Func<TSource, DecimalMeasure<T>> selector) where T : Quantity
        {
            return list.Aggregate(new DecimalMeasure<T>(0, Quantity.GetDefaultUnit<T>()), (total, item) => total + selector(item));
        }

 

And here is the inner class for Observable:

            public static IValueProvider<DecimalMeasure<T>> Sum<T>(IEnumerable<DecimalMeasure<T>> list) where T : Quantity
            {
                return Obtics.Collections.ObservableEnumerable.Aggregate(list, 0m * Quantity.GetDefaultUnit<T>(), (total, item) => total + item);
            }

            public static IValueProvider<DecimalMeasure<T>> Sum<TSource, T>(IEnumerable<TSource> list, Func<TSource, DecimalMeasure<T>> selector) where T : Quantity
            {
                return Obtics.Collections.ObservableEnumerable.Aggregate(list, 0m * Quantity.GetDefaultUnit<T>(), (total, item) => total + selector(item));
            }

            public static IValueProvider<DecimalMeasure<T>> Sum<TSource, T>(IEnumerable<TSource> list, Func<TSource, IValueProvider<DecimalMeasure<T>>> selector) where T : Quantity
            {
                // Performance: This one still has selector2 as a variable, but it's from the first lambda so I'm not sure if Obtics figures that out.
                return ExpressionObserver.Execute(list, selector, (list2, selector2) =>
                    list2.Select(s => selector2(s).Value).Sum());
            }


Hopefully this will help out some people when making their own mappings.

Coordinator
Dec 2, 2009 at 11:22 AM

Hi,

This is a very usefull example, thanks.

For performance reasons I would advise to try not to use ExpressionObserver.Execute in often called code. Try to use ExpressionObserver.Compile instead. Store the precompiled expression in a static variable an call that from your method.

            static Func<IEnumerable<TSource>, Func<TSource, IValueProvider<DecimalMeasure<T>>>, IValueProvider<DecimalMeasure<T>>> _Sum =
                ExpressionObserver.Compile(
                    (IEnumerable<TSource> list, Func<TSource, IValueProvider<DecimalMeasure<T>>> selector) =>
                        list.Select(s => selector(s).Value).Sum())
                );

            public static IValueProvider<DecimalMeasure<T>> Sum<TSource, T>(IEnumerable<TSource> list, Func<TSource, IValueProvider<DecimalMeasure<T>>> selector) where T : Quantity
            { return _Sum(list, selector); }

As an alternative you could use the lower level ObservableEnumerable methods that you used in two of your mapped methods.

            public static IValueProvider<DecimalMeasure<T>> Sum<TSource, T>(IEnumerable<TSource> list, Func<TSource, IValueProvider<DecimalMeasure<T>>> selector) where T : Quantity
            { return Obtics.Collections.ObservableEnumerable.Sum(Obtics.Collections.ObservableEnumerable.Select(list, selector)); }

Regards,

Thomas.

Dec 2, 2009 at 5:59 PM

Using the Select method works great.  Don't know why I didn't think of that in the first place. :)  Thanks for the improvement and for the caching tip!