I'm trying to query some data and projecting to a class with fewer properties by sending an expression (C# mongo driver version 2.7.3). I am trying to understand why a specific expression fails. The failure greatly limits the user from writing a common projection and forces him to write the projection inline in every call. This is a simplified example:
private IMongoCollection<MyOriginalClass> _collection;
class MyOriginalClass // imagine this class has many more properties
{
public int ID { get; set; }
}
class MyProjectedClass
{
public int ID { get; set; }
}
void DoWork()
{
var data1 = GetData(lib => new MyProjectedClass { ID = lib.ID }); // succeeds
var data2 = GetData(lib => ToProjected(lib)); // Fails in mongo driver: Index was out of range. Must be non-negative and less than the size of the collection.Parameter name: index
}
IEnumerable<MyProjectedClass> GetData(Expression<Func<MyOriginalClass, MyProjectedClass>> projection)
{
return _collection
.Aggregate()
.Project(Builders<MyOriginalClass>.Projection.Expression(projection))
.ToList();
}
MyProjectedClass ToProjected(MyOriginalClass orig)
{
return new MyProjectedClass {ID = orig.ID};
}
First (succeeded) usage is an expression which mongo driver can look into at the runtime to become to know that ID = lib.ID. Specifically here is NewExpression .
Eg Visual Studio allow for expression visualization under debugger, and for first one it shows:
.Lambda #Lambda1<System.Func`2[ConsoleApp1.Program+MyOriginalClass,ConsoleApp1.Program+MyProjectedClass]>(ConsoleApp1.Program+MyOriginalClass $lib)
{
.New ConsoleApp1.Program+MyProjectedClass(){
ID = $lib.ID
}
}
Second (failed) usage is an expression with just a call to ToProjected, ToProjected is being compiled into IL, and at the runtime mongo driver cannot retrieve the knowledge that ID = lib.ID (at least not in such an easy way as with expressions). Specifically here is MethodCallExpression . And the visualization of the second expression is:
.Lambda #Lambda1<System.Func`2[ConsoleApp1.Program+MyOriginalClass,ConsoleApp1.Program+MyProjectedClass]>(ConsoleApp1.Program+MyOriginalClass $lib)
{
.Call ConsoleApp1.Program.ToProjected($lib)
}
ToProject could be re-written as:
Expression<Func<MyOriginalClass, MyProjectedClass>> ToProjected()
{
return lib => new MyProjectedClass { ID = lib.ID };
}
And used as:
var data2 = GetData(ToProjected());
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.