Certain query does not produce notifications

Jul 26, 2009 at 11:45 AM
Edited Jul 26, 2009 at 11:46 AM

Hi,

Certain query does not seem to work (no notifications) for me, and I am not sure whether it's my undersight or an issue with Obtics.
There is a query in question:

            return ExpressionObserver.Execute(
                () => functions.Where(f => f.Word == word && f.Function.IsNotNullOrEmpty())
                                     .GroupBy(f => f.Function)
                                     .Select(g => g.Key)
                                     .FirstOrDefault()
            );

This is actually a simplified version, so it should not make sense as is (but it should work).
Now, some context:

  1. functions is a field of type ObservableCollection.
  2. f.Word is a string property which never changes.
  3. word is an argument to the method we are returning from
  4. f.Function is a property with change notifications which is changed quite often.
    Change notifications for it are not broken since other Obtics queries work correctly.

Can you help me with understanding of how I can make this query work correctly?
Thanks in advance.

Andrey Shchekin

Coordinator
Jul 27, 2009 at 2:38 PM

Hi Andrey,

I created a unit-test to verify your code and checked it in under changeset 25242. I had to make a few assumptions of which the chief one is that the Function property is of type String. In the uni-ttest obtics behaves as expected. Is the Function property of a more complex type? Note that you take the key of a group and that though the contents of a group may change the collection of groups itself may stay the same. I'm afraid I will need more information to find out what goes wrong in your case.

I notice that you use the ExpressionObserver.Execute method together with closures of the 'functions' and 'word' variables. This is fine but only when you call this statement incidentaly! The use of closures especialy makes it more likely that obtics needs to recompile your lambda expression at every call!

If you are going to execute this statement often. I'd recommend that you remove the closures and precompile the lambda expression.:

//precompile and store in a static field. Note that functions and word are now parameters to the lambda and not closures
static Func<<ObservableCollection<FClass>,string,IValueProvider<string>> _F =
    ExpressionObserver.Compile(
        (ObservableCollection<FClass> functions, string word) =>
            functions.Where(f => f.Word == word && f.Function.IsNotNullOrEmpty())
                .GroupBy(f => f.Function)
                .Select(g => g.Key)
                .FirstOrDefault()
 );
//in your function just call _F
 return _F(functions,word) ;

You can also just remove the closures and not precompile. ExpressionObserver will compile rarely but will still need to check the compilation cache every time.

//closures have become parameters
return ExpressionObserver.Execute(
    functions,
    word,
    (fncs,wrd) => 
        fncs.Where(f => f.Word == wrd && f.Function.IsNotNullOrEmpty())
            .GroupBy(f => f.Function)
            .Select(g => g.Key)
            .FirstOrDefault()
);

There is a folder with code snippets in the project that can help you create code as above.

In your statement you use the combination:

 .Where(f => f.Word == wrd && f.Function.IsNotNullOrEmpty())
        .GroupBy(f => f.Function)
        .Select(g => g.Key)
        .FirstOrDefault()

Your original code may be more complex but I think this can be simplified to:

 .FirstOrDefault(f => f.Word == wrd && f.Function.IsNotNullOrEmpty()).Function

Obtics will be so friendly not to access the Function property in case no object can be found. It will just return a default value.

Regs,

Thomas

Aug 1, 2009 at 7:06 AM
Edited Aug 1, 2009 at 7:07 AM

You are right, the simplified version does indeed work, I have found an error in my test.
The reason why it does not work in the full version seems to be due to the custom extension method.

The full code looks like this:

.Where(f => f.Word == wrd && f.Function.IsNotNullOrEmpty())         .GroupBy(f => f.Function) .HavingMax(g => g.Count())         .Select(g => g.Key)         .FirstOrDefault() 

where HavingMax is a custom extension method (from here).

Are custom IEnumerable methods supported for notifications, or am I missing something again?

Thanks for the improvement recommendations, I was going to try precompilation after I make this version work.

Coordinator
Aug 3, 2009 at 9:48 PM

Yes, custom field, property and (extension) method mappings are very possible.

Check out the "CustomMapping" example project in the source code. There a method very similar to your HavingMax method, is being mapped to an observable form.

In the example project the method is being mapped using attributes but mappings are also possible imperatively, using the ExpressionObserverMaster object. 

Regs,

Thomas.

 

Coordinator
Aug 5, 2009 at 4:53 PM

By the way; If you would publish your library of linq extensions with ExpressionObserver mappings then I would like to refer to it from the Obtics pages.

Regs,

Thomas.