Search code examples
c#odataazure-cosmosdb

Odata - How to filter on collection property of type enum


I'm attempting to filter my entities on a property which is a collection of enum variants. If the collection contains the variant I'm interested in, I want to return that entity.

My backing store is Cosmos DB and currently I'm storing the enum variants as an array of string (I'm not tied to that).

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum FsmoRole 
{
    RidMaster,
    PdcEmulator,
    // Other FSMO roles
}

public class DomainController
{
    public IList<FsmoRole> FsmoRole { get; set; } =
        new List<FsmoRole>():
}

The query I was attempting to execute was the following:

$filter=fsmoRoles/any(f: f eq RidMaster)

This returns the following error from the Odata API:

Microsoft.OData.ODataException: Could not find a property named 'RidMaster' on type 'DomainController'

And, if I use quotes around the variant I'm interested I get back no results:

$filter=fsmoRoles/any(f: f eq 'RidMaster')

One solution I could use instead is to use a collection of string's to represent the FSMO roles. This would allow me to do the following:

$filter=fsmoRoles/any(f:contains(f, 'RidMaster'))

And this works and is a viable option, but I wanted to know if I could perform this query without having to change the entities FsmoRoles property to a collection of string's.


Solution

  • The issue is, you're storing the enum variants as string's in the backing store.

    If you look at the generated Expression tree, you'll see that it's converting your input values to Int32 and then attempting the comparison in the DB.

    .Where($it => $it.FsmoRoles.Any(f => (Convert(f, Int32) == 2)))
    

    Last time I checked the value 2 is not equal to "RidMaster". This is why you're not seeing results. I would stop storing the collection of FsmoRoles as string's and instead store them as int's.

    When returning the results from your controller you can serialize the enum variants to string.

    Another option (if you don't want to store the FsmoRoles as int's) is to use the cast operator.

    Your query would then look something like this:

    $filter=fsmoRoles/any(f: cast(f, Edm.String) eq 'RidMaster')