Reputation: 3484
When querying for sub entities from the client via WCF Data Services, the .All()
query returns base entities that have no sub entities. For example, imagine a database that only has two Customers in it. Orders are a sub entity with a BuyerName column. One customer record has two orders from two different buyers and the other customer has no orders.
The following query will not return the customer with two orders because they were two different buyers (this is as expected) BUT it does return the customer that doesn't have any orders!
query.Where(c => c.Orders.All(
o => o.BuyerName == buyer.Name));
By adding an extra .Any()
query, I get what I would have thought the .All()
query would have done for me automatically.
query.Where(c => c.Orders.All(
o => o.BuyerName == buyer.Name)
&& c.Orders.Any());
This seems to violate the principle of least surprise. I would think that calling .All()
would require that no base entity be returned when no sub entities are present. So, why does .All()
within WCF Data Services client return base entities when no sub entity exists?
Upvotes: 0
Views: 114
Reputation: 4555
Forget about OData and WCF Data Services for a sec. What should this code produce?
IEnumerable<string> emptyEnumerable = Enumerable.Empty<string>();
bool result = emptyEnumerable.All(item => item == "foo");
If I understand your question correctly, you're expecting result
to be false, since there's no item in the list. But result
will in fact be true. In the MSDN documentation, you can see that the return value of .All()
is documented as
"true if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, false."
As far as I've seen, this is the behavior for any language where something like All()
exists (and I bet there's something in the foundations of predicate logic that requires this).
So with this in mind, WCF Data Services and OData are doing exactly the right thing and following the same contract that's used everywhere else. c.Orders.All(predicate)
should always return true if c.Orders
is empty. In your first query, that means that any c
such that c.Orders
is empty will pass the filter predicate. Like you mentioned, you can make the filter predicate stronger by asserting that there is at least one item in c.Orders
by requiring c.Orders.Any()
to also be true.
Upvotes: 3