I'm trying to make a simple REST api with the ability to filter resources with some simple url query parameters e.g /Profile?firstName=Brian&age=26. I don't need any advanced filtering just find anything that matches all.
I'm using Minimal APIs in .NET 6 and MongoDB.Driver (2.17.1).
I've got a "solution" that works but its error prone and isn't very scalable or flexible.
How would like be able to read the query parameters without hardcoding the specific name and type for each endpoint without losing the type of the parameter. If possible use the Filter builder instead of json.
Current "solution"
Part of the Profile class
public class Profile : Entity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
...
public List<BasePreference> Preferences { get; set; }
public Profile()
{
Preferences = new List<BasePreference>();
}
}
App.MapGet("/profile",Test2);
private static async Task<IResult> Test2(
IRepository<Profile> repo,
string? firstName,
string? lastName,
int? age)
{
Dictionary<string, object?> queryDict= new Dictionary<string, object?>();
queryDict.Add(nameof(firstName),firstName);
queryDict.Add(nameof(lastName),lastName);
queryDict.Add(nameof(age), age);
foreach (var pair in queryDict)
{
if(pair.Value is null)
{
data.Remove(pair.Key);
}
}
var query = JsonSerializer.Serialize(data);
var content = await repo.ReadMany(query);
return Results.Ok(content);
}
MongoDB Repository snippet
private IMongoCollection<T> collection;
public MongoDBRepository(IOptions<MongoDBSettings> mongoDBSettings)
{
MongoClient client = new MongoClient(mongoDBSettings.Value.ConnectionURI);
IMongoDatabase database = client.GetDatabase(mongoDBSettings.Value.DatabaseName);
collection = database.GetCollection<T>(typeof(T).Name.ToLower());
}
public async Task<List<T>> ReadMany(string query)
{
FilterDefinition<T> filter = query;
List<T> list = await collection.Find(filter).ToListAsync();
return list;
}
My problems with this approach is manifold:
- Repeatedly implementing this solution for multiple resources would be mind-numbing and error prone
- Capitalization needs to match inside MongoDB exactly
- Changes to Model classes would require multiple changes in different parts of the project
- It can't handle any "complex" data types. e.g DateTime, Guid.
Things I've tried
I've tried just converting everything to a Json string and ignoring types, but age:"25" =/= age:25 according to MongoDB.
I've then searched for ways to allow "25" == 25 in mongoDB but haven't had any luck
I've tried creating a
ProfileQueryModelthat only contains the queryable properties and then using[FromParameters]but couldn't get the binding to work, and was also unsure how to convert it to aFilterDefinitionI've tried mapping the search query to a
Dictionary<string,object>but with no luck. I've read that .NET 7 might include a way to make that possible, but that hasn't released yet.I've tried having the query parameters just be a single JSON object e.g
/profiles?query={"firstName":"Brian","age":25"}but was once again stuck on isn't a string
What I'm looking for
I don't need to be able to filter elements in arrays or sub objects. Just simple equality on all given fields.
Ideally, I'd want a solution where I don't use Json as a FilterDefinition as that seems less likely to break with more advanced data types and would also remove the need for any Json conversions. But I've been pulling my hair out for over a week now and I'm happy with any improvement I can get.
Thanks in advance and thanks for reading this far.
