Convert lambda expressions to json objects using MongoDB.Driver

I need to execute my queries as string instead of lambda expressions because I am having a hard time using mocking frameworks in order to create unit tests for my projects.

In other words, I want to modify my IDatabase interface from:

Interface IDatabase 
{
    IEnumerable<User> Find(Expression<Func<User, bool>> filter);
}

To:

Interface IDatabase 
{
    IEnumerable<User> Find(string query);
}

I already have a lot of my queries written with expressions. As a result, I have created this code to convert an expression to a JSON object:

using MongoDB.Driver;
using System.Linq.Expressions;

class Program
{

    // Example of a collection I store on my DB
    class User
    {
        public string _id { get; set; }

        public string Name { get; set; }

        public DateTime DateCreated { get; set; }
    }

    // Main method
    public static void Main()
    {
        var json = ExpressionToJson<User>(x => x.Name.Contains("Tono") && x.DateCreated < DateTime.UtcNow);

        // outputs:
        // { "Name" : /Tono/s, "DateCreated" : { "$lt" : ISODate("2022-01-21T01:21:27.975Z") } }
        Console.WriteLine(json);

    }

    /// <summary>
    ///     Method that will convert an expression to a string
    /// </summary>
    public static string ExpressionToJson<T>(Expression<Func<T, bool>> filter)
    {
        MongoClient MongoClient = new MongoClient();

        var db1 = MongoClient.GetDatabase("DoesNotMatter");

        var collection = db1.GetCollection<T>("DoesNotMatter");

        var query = collection.Find(filter);

        var json = query.ToString();

        if (string.IsNullOrEmpty(json))
            return "{}";

        // json should look something like this
        // find({ "Name" : /Tono/s, "DateCreated" : { "$lt" : ISODate("2022-01-21T01:11:47.772Z") } })

        // remove the find( at the beginning and last parenthesis
        if (json.StartsWith("find("))
            return json.Substring(5, json.Length - 6);

        throw new NotImplementedException("Did serializer changed?");
    }

}

As you can see this code can convert the expression

x => x.Name.Contains("Tono") && x.DateCreated < DateTime.UtcNow

to JSON

{ "Name" : /Tono/s, "DateCreated" : { "$lt" : ISODate("2022-01-21T01:24:38.628Z") } }

How can I simplify the ExpressionToJson method? It will be cool if I could avoid having to create an instance of MongoClient then an instance of a Database then an instance of IMongoCollection<TDocument> to just serialize the expression as I need it.


Solution 1:

Perhaps, you can write an IMongoCollection extension method and adopt IFindFluent<T, T>.Filter.Render (similar to this question) to generate the filter query in JSON.

public static class IMongoCollectionExtensions
{
    public static string ExpressionToJson<T>(this IMongoCollection<T> collection, Expression<Func<T, bool>> filter)
    {
        var query = collection.Find(filter);

        return query.Filter.Render(
            collection.DocumentSerializer,
            collection.Settings.SerializerRegistry
        ).ToJson();
    }
}
var jsonQuery = collection.ExpressionToJson<User>(x => x.Name.Contains("Tono")
    && x.DateCreated < DateTime.UtcNow);

Output