Search code examples
c#mongodblinqoperator-overloadingequality

MongoDB C# Equality override isn't picked up


I have the following blocks of code:

    public bool Equals(TranslatedTextReference other)
    {
        if (other is null) return false;
        return Translations.Equals(other.Translations);
    }

    public bool Equals(string other)
    {
        foreach(KeyValuePair<string, string> pair in Translations)
        {
            if (other.Equals(pair.Value, StringComparison.InvariantCultureIgnoreCase))
                return true;
        }
        return false;
    }

    public static bool operator ==(TranslatedTextReference left, TranslatedTextReference right) => left.Equals(right);
    public static bool operator !=(TranslatedTextReference left, TranslatedTextReference right) => left.Equals(right);

    public static bool operator ==(TranslatedTextReference left, string right) => left.Equals(right);
    public static bool operator !=(TranslatedTextReference left, string right) => !left.Equals(right);

which overrides the Equlas and == behavior in a custom class named TranslatedTextReference. And

database.GetCollection<Element>("elements").AsQueryable()
            .FirstOrDefault(element => element.TranslatedElementName == name);

which in theory should query Mongo and use the overriden == operator. In practice this doesn't work, as (I assume) the overriden operator isn't actually called because the LINQ query is executed as a MongoDB query directly on the MongoDB server. In fact this other piece of code which uses default equality operators work perfectly.

database.GetCollection<Element>("elements").AsQueryable()
            .FirstOrDefault(element => element.AtomicNumber == atomicNumber);

AtomicNumber is an int.

Is there a way to make MongoDB uses my custom operator?

Also, I know I could just use default operators and build the same query with those, but I must use == or .Equals() in my LINQ query.


Solution

  • There is no way to achieve this behaviour which is due to the fact that the driver only "knows" certain kinds of methods and is able to translate them into MongoDB queries.

    Imagine you wrote all sorts of funky code in your custom Equals implementation (like call a webservice or log some stuff or whatever). What would the driver do in order to translate this into a MongoDB query?

    When you look into the driver code you'll see that the translation logic is simply based on the name of the respective method in the Expression Tree that you pass as a predicate:

    private FilterDefinition<BsonDocument> TranslateMethodCall(MethodCallExpression methodCallExpression)
    {
        switch (methodCallExpression.Method.Name)
        {
            case "Contains": return TranslateContains(methodCallExpression);
            case "ContainsKey": return TranslateContainsKey(methodCallExpression);
            case "EndsWith": return TranslateStringQuery(methodCallExpression);
            case "Equals": return TranslateEquals(methodCallExpression);
            case "HasFlag": return TranslateHasFlag(methodCallExpression);
            case "In": return TranslateIn(methodCallExpression);
            case "IsMatch": return TranslateIsMatch(methodCallExpression);
            case "IsNullOrEmpty": return TranslateIsNullOrEmpty(methodCallExpression);
            case "StartsWith": return TranslateStringQuery(methodCallExpression);
        }
    
        return null;
    }