efcore
efcore copied to clipboard
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:
-
IsDefined
in aWhere
call -
IsDefined
in aSelect
call that creates an anonymous type -
IsDefined
on a shadow property (Person.__id
) -
IsDefined
on a derived property (Bird.IsFlightless
in InheritanceQueryCosmosTest) -
IsDefined
on an embedded scalar property (OwnedPerson.PersonAddress.Country.Name
in OwnedQueryCosmosTest) -
IsDefined
on an embedded scalar collection -
IsDefined
on an embedded reference navigation (OwnedPerson.PersonAddress
in OwnedQueryCosmosTest, can throw) -
IsDefined
on an embedded collection navigation (OwnedPerson.Orders
in OwnedQueryCosmosTest, can throw) -
IsDefined
on a non-embedded navigation (Person.Orders
, should throw) -
IsDefined
on a non-existing property (Person.NonExisting
, should throw) -
IsDefined
on 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.