ComponentOne LiveLinq > LiveLinq Programming Guide > Query Expressions Supported in Live Views |
Apart from the limitations on query operators, a query must satisfy an additional condition in order to be used as a live view. Fortunately, this condition is satisfied in most cases, so you should care about it only if your query contains some special expressions, which is relatively rare (except that all classes used in LiveLinq to Objects must satisfy the property notification condition, but that was already mentioned in Using the built-in collection class IndexedCollection (LiveLinq to Objects). Unfortunately, this condition is not verified by LiveLinq automatically, so it is your responsibility to make sure it is satisfied. If this condition is not satisfied, your view will not react to changes in its base data. We give two descriptions of this condition: one short, for basic understanding, and another detailed, for advanced usage.
Short Description
The short answer is, basically, that in some cases your classes need to provide property notifications. There are two such cases where you need to care about that:
(1) LiveLinq to Objects.
There your views' arguments are collections of your own classes, so you must make sure that your classes provide property notifications, otherwise your views will not react to changes of those properties,(see Using the built-in collection class IndexedCollection (LiveLinq to Objects).
(2) Views over views and indexes over views (applies to LiveLinq to DataSet and LiveLinq to XML as well as to LiveLinq to Objects).
If you have a view
View<T> v;
and want to create another view over view v (use v as its argument) or create an index over v, then make sure that the T class has property notifications, otherwise the view (or index) you define over v will not react to changes of those properties.
Note for LiveLinq to DataSet and LiveLinq to XML: If the view result class T is (or is derived from) DataRow (for LiveLinq to DataSet) or XNode (for LiveLinq to XML), then property notifications there are not needed (so there are no conditions or restrictions in this case, it just works), LiveLinq gets the notifications it needs from the standard events.
Also, it can be mentioned here that you don't need to care about these conditions if the properties of your objects are not changed by your code in other ways than automatically by LiveLinq itself; that is, if you don't have code setting properties of the objects that are elements in the source collections of your views. In particular, if your classes have read-only properties, which includes all anonymous classes, that are often used in LINQ. It is also the case if you use structs, that is, value types (as opposed to classes, reference types), because value types are not references, so you can't change the elements of your collection. In all these cases there is no need in change notifications.
And finally, here is what you need to avoid: A possible source of errors is using property chains, such as for an Order class with a Customer property:
o => o.Customer.City
You can do it if you do not modify the City property in Customer objects. But if you have code changing the City property, LiveLinq will not reflect the change, because the Order object usually does not notify it of changes in Customer objects. Note that you can usually modify the query so it has the same effect without using property chains. For example, instead of
…Select(o => o.Customer.City)
use
…Select(o => o.Customer).Select(c => c.City)
Detailed Description
Observable and non-observable functions
Our condition limits the choice of functions (such as result selector, key selector, et cetera) that you can use in your query expression depending on whether or not your classes support property notifications, see Using the built-in collection class IndexedCollection (LiveLinq to Objects).
The property notifications we are talking about are those in the element classes of your view's arguments, not in the class of your view's result (but, of course, one view's result can be another view's argument, since views can be created based on views).
We distinguish two types of functions: observable and non-observable. Observable functions are allowed, non-observable functions must be avoided.
Examples of observable functions:
x => x.P1
x => x.P1 + x.P2 + x.M(c1)
(x, y) => new {x.P1, x.P2, M = y.M(), y.P3}
Here P1, P2, P3, M are properties/methods with change notifications, and c1 is a value that never changes.
Note for advanced usage: Property/method call is understood here to include not only one applied directly to one of this function's parameters, but also, recursively, to the parameters of a function preceding this function in the query, if they can be reached through (possibly a chain of) simple references in object initializers. For example,
....Select((x, y) => new { P1 = x, P2 = y }).Select(z => new { z.P2.A, z.P1.B })
is allowed (if, of course, properties A and B are observable).
1a. Constants are also observable.
Note that we specifically excluded constant values from the condition above. Any constant values/objects are allowed in a function and don't break its observability. By "constant value", we mean that it remains the same object for every given parameter value. In other words, it does not change with time. The most common example of a constant is the identity function:
x => x
Although the state of the object can change with time, the object itself, as well as the reference to it, stays the same (for a given parameter object, of course, which in this case is the same as the result object), therefore it is a constant. Other examples of constants are:
x => c1
(x, y) => new {x, y}
(x, y) => x + y
x => x == c1 ? c2 : c3
where c1, c2, c3 are some constant values.
Constant values can be combined in the same function with observable values, and the resulting function remains observable, for example:
x => new {x, x.P1, P = y.P2 + y.P3 }
where P1 and P2 are properties with change notifications.
1b. Object initializers and constructors that don't depend on the arguments are allowed.
The previous examples included only new with anonymous classes, but, in fact, user-defined classes and constructor calls are also allowed without breaking observability, as long as you don't use the function arguments in the constructor, using only constant expressions or nothing at all (a parameterless constructor or an object initializer). So, the following functions are observable (where c1 is a constant value and P and Q are observable properties):
(x, y) => new C { x.P, y.Q}
(x, y) => new C(c1) { X = x, P = y.P }
and the following are non-observable:
(x, y) => new C(y) { x.P, y.Q}
(x, y) => new C(c1, x) { x, y.P }
As the name suggests, any function that does not satisfy the condition above is considered non-observable. Note that this condition of observability depends not only on the function itself but also on the class of the argument of that function; that class must have property change notifications for everything that is used in the function that is not constant. So, even the simplest functions, such as x => x.P can be non-observable if P is non-observable (that is, the class does not issue change notifications for P). So, the first and most common examples of non-observable functions are the same as above but in situations where at least one of the properties P1, P2 is not observable, that is, the class does not provide change notifications for it:
x => x.P1
(x, y) => new {x.P1, y.P2 }
This can occur if you simply forgot to add the code providing notifications (see Using the built-in collection class IndexedCollection<T> (LiveLinq to Objects)|tag=Using_the_built_in_collection_class_IndexedCollectionT_LiveLinq_to_Objects).
Another possible cause is a property returning a calculated value, like in
public class Customer
{
public List<Order> orders;
public int OrderCount { get {return orders.Count;} }
}
unless you specifically take care to issue a property change notification in the Customer class every time its orders.Count changes.
Yet another possible cause is using a chain of properties, like, for an Order class with a Customer property:
o => o.Customer.City
Note that any expression, including the two above, can be made observable if you take special care to trigger property change notification every time its value changes, but it requires code written specifically for that purpose. For example, with the last function, you can trigger property change notification events for the Orders collection every time the City property in the Customer class changes.