简体   繁体   中英

C# Mongo DB driver - expression with method call not working

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM