Just ran into an odd issue when trying to do something fairly simple with the Mongo C# driver (2.9.2) on .NET Core 3.1, so wanted to clarify whether it was a bug or known limitation of the Mongo-LINQ IQueryable implementation (or I’m doing something bad!)
We have a fairly simple collection containing documents with 4 fields, including a Bson ObjectId and a Guid. I am trying to expose this as a IQueryable, however in one of our API calls we process this with a .Where() clause and a .Select(x => new ApiClass(x)) where ApiClass has a single constructor which takes an instance of the (this doesn’t do much - converts the GUID to a string and hides some fields).
Evaluating the results of this query results in a crash when doing it in unit tests with the following error:
System.NotSupportedException
Could not find a member match for constructor parameter alert on type ApiClass in the expression tree new ApiClass({document}).
at MongoDB.Driver.Linq.Processors.ProjectionMapper.VisitNew(NewExpression node)
I half suspect it’s trying to pass in the document raw rather than as an instantiated , and can’t find the constructor for that but I’m guessing.
Some snippets of code:
// The Mongo entity
public class Alert
{
public Alert()
{
}
public Alert(Guid userId, string publicationId)
{
UserId = userId;
PublicationId = publicationId?.Trim();
IsActive = true;
}
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonRequired]
public Guid UserId { get; set; }
[BsonRequired]
public string PublicationId { get; set; }
[BsonRequired]
public bool IsActive { get; set; }
}
// Return structure for the API
public class ApiAlert
{
public ApiAlert(CHub.Bff.Shared.MongoDb.Entities.Alert alert)
{
Id = alert.Id;
UserId = alert.UserId.ToString();
PublicationId = alert.PublicationId;
IsActive = alert.IsActive;
}
public string Id { get; set; }
public string UserId { get; set; }
public string PublicationId { get; set; }
public bool IsActive { get; set; }
}
// API call
public IQueryable<ApiAlert> Alerts([Service] IResolverContext resolverContext, string? userId = "")
{
// alerts is IQueryable<Alert>
var alerts = alertsService.GetAlerts();
if (!string.IsNullOrEmpty(userId))
{
if (!Guid.TryParse(userId.Trim(), out var userGuid))
{
// some code to report the error
return null;
}
alerts = alerts.Where(alert => alert.UserId == userGuid);
}
return alerts.Select(alert => new ApiAlert(alert));
}