I have a function I use to retrieve an object by its ID from Mongo DB and it looks like this:
public async Task<IEnumerable<TItem>> GetItemsAsync(uint limit,
Expression<Func<TItem, bool>> filter = null,
SortDefinition<TItem> sort = null,
ProjectionDefinition<TItem> projection = default,
CancellationToken token = default) {
// First, create the options describing which objects will be returned
var findOptions = new FindOptions<TItem> {
Limit = (int)limit,
Sort = sort,
Projection = projection
};
// Next, connect to the specific collection in the database
IMongoCollection<TItem> collection = GetCollection();
// Now, attempt to find all the items associated with the filter and find options
IAsyncCursor<TItem> cursor = await collection.FindAsync(filter ?? FilterDefinition<TItem>.Empty,
findOptions, token).ConfigureAwait(false);
// Finally, retrieve all the items and return them
return await cursor.ToListAsync(token).ConfigureAwait(false);
}
The problem I'm having is that this breaks in the case where I want to return all the fields from the document and therefore do not provide a projection. In such cases, I get the following exception:
System.ArgumentNullException: Value cannot be null. (Parameter 'projection')
at MongoDB.Driver.Core.Misc.Ensure.IsNotNull[T](T value, String paramName)
at MongoDB.Driver.KnownResultTypeProjectionDefinitionAdapter`2..ctor(ProjectionDefinition`1 projection, IBsonSerializer`1 projectionSerializer)
at MongoDB.Driver.ProjectionDefinition`2.op_Implicit(ProjectionDefinition`1 projection)
My question is: is there a pass-through projection definition or a way to programmatically map all the fields retrieved from the MongoDB document onto my return object?
Thanks to @Llama's suggestion, I was able to fix this. I refactored my code to look like this:
public async Task<IEnumerable<TItem>> GetItemsAsync(uint limit,
FilterDefinition<TItem> filter = null,
SortDefinition<TItem> sort = null,
ProjectionDefinition<TItem, TItem> projection = null,
CancellationToken token = default) {
return await CreateFindOperation(limit, filter, sort, projection)
.ToListAsync(token).ConfigureAwait(false);
}
// Helper function that creates a find-operation from some common elements
private IFindFluent<TItem, TItem> CreateFindOperation(uint? limit,
FilterDefinition<TItem> filter = null,
SortDefinition<TItem> sort = null,
ProjectionDefinition<TItem, TItem> projection = null) {
// First, connect to the specific collection in the database
IMongoCollection<TItem> collection = GetCollection();
// Next, create an operation to find the items that are associated with the filter
IFindFluent<TItem, TItem> operation = collection.Find(filter ?? FilterDefinition<TItem>.Empty);
// Now, if the projection operation has been provided then apply it to the
// search and then update the search
if (projection != null) {
operation = operation.Project(projection);
}
// If the sort operation has been provided then modify the search with the sort operation
if (sort != null) {
operation = operation.Sort(sort);
}
// Finally, if the limit has been included in the request then modify the operation to only
// return the first N items associated with the limit; otherwise, return the operation as-is
return limit.HasValue ? operation.Limit((int)limit) : operation;
}
The helper function allows me to reuse the code for other retriever-style functions as well.