Cosmos: Add support for IS_DEFINED() to EF.Functions
The full context is explained in #13131, but I decided to create a new issue to gather more targeted customer feedback on this.
The short version is that in a schema-less database like Cosmos DB it is valid for each document in a container to contain completely different set of properties. In an object mapping situation this translates into some of the mapped properties being absent. Cosmos DB provides a function IS_DEFINED() that can be used in queries to test for this situation. Note also that a property not being defined is different (albeit one can argue only slightly) from the value being null.
The Cosmos DB provider could define an extension method in EF.Functions that could be used, for example, like this:
var itemsMissingData = context.Items.Where(i => !EF.Functions.IsDefined(i.Data));
It seems that Cosmos DB is still filtering out documents where a property is undefined if you ORDER BY that property. This makes providing access to the IS_DEFINED() function from LINQ queries more important. That way, you can use it like this:
var orderedItems = context.Items.OrderBy(i => i.Data);
var itemsMissingData = context.Items.Where(i => !EF.Functions.IsDefined(i.Data));
orderedItems.AddRange(itemsMissingData);
Also consider Coalesce operator ?? which is used to coalesce based on present keys.
Do we have a timeline on when this will be implemented?
@prachita-mane It's in the backlog meaning we don't plan on working on it in the upcoming releases. However we are open to well-written external PRs.
Is there an alternative to this? For example I am trying to check if a nested property is null and am getting an exception about the query not being able to be translated. My query looks like the following where .Provider is a complex type. I only want to return types where there isn't a Provider field set.
.Where(p => p.Provider == null)
This throws the following exception:
DbSet<User> .Where(u => EF.Property<Nullable<Guid>>(EF.Property<Provider>(u, "Provider"), "UserId") == null)' could not be translated.
Are we dealing with the same/similar thing here?
I would have expected the translation for CosmosDB to be as simple as
WHERE c["Provider"] == null
Note to implementer, add these test cases:
-
IsDefinedin aWherecall -
IsDefinedin aSelectcall that creates an anonymous type -
IsDefinedon a shadow property (Person.__id) -
IsDefinedon a derived property (Bird.IsFlightlessin InheritanceQueryCosmosTest) -
IsDefinedon an embedded scalar property (OwnedPerson.PersonAddress.Country.Namein OwnedQueryCosmosTest) -
IsDefinedon an embedded scalar collection -
IsDefinedon an embedded reference navigation (OwnedPerson.PersonAddressin OwnedQueryCosmosTest, can throw) -
IsDefinedon an embedded collection navigation (OwnedPerson.Ordersin OwnedQueryCosmosTest, can throw) -
IsDefinedon a non-embedded navigation (Person.Orders, should throw) -
IsDefinedon a non-existing property (Person.NonExisting, should throw) -
IsDefinedon a non-mapped property (Person.__jObject, should throw)
Note also #33904, which is about adding support for the undefined coalescing operator (??).
Poaching as I'm in the area with #33904.