Part of the perceived awkwardness probably comes from mixing models. The C# driver lets you work with queries either at a low level with QueryDocument (similar to writing a query in JSON in the mongo shell) or at a higher level using the Query builder. When building dynamic queries you are probably better off working exclusively at one level, either like this:
var query = new QueryDocument();
if (!string.IsNullOrEmpty(agency))
{
query.Add("Agency", agency);
}
if (categories != null && categories.Count != 0)
{
query.Add("Categories", new BsonDocument("$in", categories));
}
Or if you wanted to do something similar entirely at a slightly higher level using the Query builder you could write this:
IMongoQuery query = new QueryDocument();
if (!string.IsNullOrEmpty(agency))
{
query = Query.And(query, Query.EQ("Agency", agency));
}
if (categories != null && categories.Count != 0)
{
query = Query.And(query, Query.In("Categories", categories));
}
In both cases you'd want to think about what it means if you end up with an empty query (if none of the if statements match).