【问题标题】:C# Mongo DB driver - expression with method call not workingC# Mongodb 驱动程序 - 带有方法调用的表达式不起作用
【发布时间】:2019-04-15 13:53:11
【问题描述】:

我正在尝试通过发送表达式(C# mongo 驱动程序版本 2.7.3)查询一些数据并投影到具有较少属性的类。 我试图了解为什么特定表达式会失败。 该失败极大地限制了用户编写通用投影并迫使他在每次调用中内联编写投影。 这是一个简化的例子:

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};
}

【问题讨论】:

标签: c# database mongodb linq-expressions mongodb-csharp-2.0


【解决方案1】:

第一次(成功)使用是一个表达式,mongo 驱动程序可以在运行时查看以知道 ID = lib.ID。具体这里是NewExpression

例如Visual Studio 允许在调试器下进行表达式可视化,第一个显示:

.Lambda #Lambda1<System.Func`2[ConsoleApp1.Program+MyOriginalClass,ConsoleApp1.Program+MyProjectedClass]>(ConsoleApp1.Program+MyOriginalClass $lib)
{
    .New ConsoleApp1.Program+MyProjectedClass(){
        ID = $lib.ID
    }
}

第二次(失败)使用是一个仅调用 ToProjected 的表达式,ToProjected 正在被编译到 IL 中,并且在运行时 mongo 驱动程序无法检索 ID = lib.ID 的知识(至少不是以这种简单的方式与表达式一样)。具体来说这里是MethodCallExpression。而第二个表达式的可视化是:

.Lambda #Lambda1<System.Func`2[ConsoleApp1.Program+MyOriginalClass,ConsoleApp1.Program+MyProjectedClass]>(ConsoleApp1.Program+MyOriginalClass $lib)
{
    .Call ConsoleApp1.Program.ToProjected($lib)
}

ToProject 可以重写为:

Expression<Func<MyOriginalClass, MyProjectedClass>> ToProjected()
{
    return lib => new MyProjectedClass { ID = lib.ID };
}

并用作:

var data2 = GetData(ToProjected());

【讨论】:

  • 那么编译表达式(Func)和MethodCallExpression没有区别吗? MethodCallExpression 无法遍历?
  • 嗯,这没什么区别,MethodCallExpression 只是调用编译后的代码,作为表达式树,它非常浅,在运行时提供的信息很少。相比之下,NewExpression 可能是具有更多信息的更大的表达式树。是的,MethodCallExpression 不能被遍历(无需反编译)
猜你喜欢
  • 2022-11-28
  • 1970-01-01
  • 2017-03-17
  • 1970-01-01
  • 1970-01-01
  • 2016-03-15
  • 1970-01-01
  • 2021-04-23
相关资源
最近更新 更多