C# Driver serializing enums as ints in aggregation pipelines (but as strings everywhere else)

I’m having a problem where I can’t use aggregations in C# (at least, not with lambdas - obviously I could manually make BsonDocuments) because enum values are being serialized as their int values instead of as strings. Here I have a small example to replicate the problem

using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;

enum Words
{
    Foo, Bar
}

class MongoEntity
{
    [BsonId]
    public Guid _id { get; set; }

    [BsonRepresentation(BsonType.String)]
    public Words Word { get; set; }
}

class Summary
{
    public int BarCount {get;set;}
}

class Program
{
    public static void Main(string[] args)
    {
        var client = new MongoClient("mongodb://****:*****@localhost");
        var database = client.GetDatabase("test");
        database
            .GetCollection<MongoEntity>("collection")
            .DeleteMany(Builders<MongoEntity>.Filter.Empty);
        database
            .GetCollection<MongoEntity>("collection")
            .InsertMany(new[]
            {
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Foo },
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Foo },
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Foo },
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Foo },
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Foo },
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Foo },
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Foo },
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Bar },
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Bar },
                new MongoEntity { _id = Guid.NewGuid(), Word = Words.Bar },
            });

        var entities = database
            .GetCollection<MongoEntity>("collection")
            .Find<MongoEntity>(Builders<MongoEntity>.Filter.Empty)
            .ToCursor()
            .ToList();
        var group = database
            .GetCollection<MongoEntity>("collection")
            .Aggregate()
            .Group(
                _ => 1,
                data => new Summary
                {
                    BarCount = data.Count(row => row.Word == Words.Bar)
                });
        var summary = group.ToCursor().First();

        Console.WriteLine(string.Join(", ", entities.Select(e => e.Word)));
        Console.WriteLine(group.ToString());
        Console.WriteLine(summary.BarCount);
    }
}

The output I get when I run this is:

Foo, Foo, Foo, Foo, Foo, Foo, Foo, Bar, Bar, Bar
aggregate([{ "$group" : { "_id" : 1, "BarCount" : { "$sum" : { "$cond" : [{ "$eq" : ["$Word", 1] }, 1, 0] } } } }])
0

I’m not sure if this is a bug in the driver or if I’m configuring something wrong. If it’s the latter, I appreciate any and all help!

I realize I wasn’t entirely clear on why the output is unexpected -

  • line 1 shows the enum is correctly being serialized as a string when inserting data
  • in line 2, we can see the grouping is incorrect - in the middle where it says "$eq" : ["$Word", 1], it’s supposed to say "$eq" : ["$Word", "Bar"]
  • line 3 also shows the grouping is incorrect - the result should be 3, but it is 0 (because no row in the database has Word == 1)

Hi, @Jordan_Pitlor,

Welcome to the MongoDB Community Forums! I understand that you’re having problems with enums not being represented correctly in queries. This appears to be CSHARP-3449, which was fixed in our new LINQ3 provider. Although you’re using aggregations, we use the LINQ3 provider to implement expression translation.

At the moment, LINQ3 is opt-in only, but will become the default in an upcoming version of the driver. You can opt into the LINQ3 provider as follows:

var settings = new MongoClientSettings { LinqProvider = LinqProvider.V3 };
var client = new MongoClient(settings);

Please let us know if this resolves your issue.

Sincerely,
James

Thanks for the response! That is indeed the exact issue.

I changed the first few lines of Main to the following:

var settings = MongoClientSettings.FromConnectionString("mongodb://*****:*****@localhost");
settings.LinqProvider = LinqProvider.V3;
var client = new MongoClient(settings);

But now I’m getting a runtime error saying ValueType IGrouping<Words, ...> of parameterSerializer does not match parameter type IGrouping<Int32, ...>.

Looking briefly at the source, it seems the driver is correctly expecting the enum type, but the lambda is getting casted to use ints somewhere? Does this suggest there’s an attribute or explicit type I should be adding to the aggregation call?

@James_Kovacs - tagging you not because I expected a response already, but just in case you needed a notification to know the V3 provider didn’t fix the problem. I appreciate all your help so far!

Hi, @Jordan_Pitlor,

Thank you for trying your aggregation with LINQ3. I was able to reproduce the failure using the 2.15.0 driver, but not with latest. We have made a number of LINQ3-related bug fixes and improvements since 2.15.0, but they are only available in our master branch. Here are the results of running your repro using master:

Foo, Foo, Foo, Foo, Foo, Foo, Foo, Bar, Bar, Bar
aggregate([{ "$group" : { "_id" : 1, "_elements" : { "$push" : "$$ROOT" } } }, { "$project" : { "BarCount" : { "$size" : { "$filter" : { "input" : "$_elements", "as" : "row", "cond" : { "$eq" : ["$$row.Word", "Bar"] } } } }, "_id" : 0 } }])
3

We will be releasing a new version with these fixes in the coming weeks, which should resolve your issue. Thank you for your patience.

Sincerely,
James

Awesome, I’ll keep an eye out for that release. Thanks so much!

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.