【问题标题】:Access property of a generic type inside lambda expression访问 lambda 表达式中泛型类型的属性
【发布时间】:2019-08-19 08:57:18
【问题描述】:

标题可能有歧义,但让我解释一下。

我正在使用 MongoDb 和 MongoDb 的 c# 驱动程序,我们可以创建一个 FilterDefinition<T> 来创建一个过滤器来获取数据,如下所示:

var Filter = Builders<TestClass>.Filter.Eq(x => x.AnyProperty, Value);

我正在尝试将此代码放入可重用的通用方法中,这样我就不会一次又一次地编写相同的代码。我不会在这里包含整个函数,但在函数内部,我正在尝试执行以下操作:

var Filter = Builders<T>.FIlter.Eq(x => x.AnyProperty, value);

现在的缺点是:

  • T 是一个泛型类型,这意味着它没有我正在寻找的属性。因此,我尝试获取 T 的类型并按名称获取我要查找的属性,如下所示:

    ...FIlter.Eq(x =&gt; x.GetType().GetProperty(PropertyName), value)

这会导致异常:

无法确定 x => x.GetType().GetProperty("UserName") 的序列化信息。 // UserName 是属性名

所以,我的问题是,我可以在这里为泛型类型做什么,它相当于 lambda 表达式中的x =&gt; x.PropertyName

更新

忘了说,我确实试过这个:

var Filter = Builders<T>.FIlter.Eq("PropertName", value);

但它不会从数据库中返回结果,因为这样做:

var Filter = Builders<MyClass>.FIlter.Eq("PropertName", value);

我真的很想知道为什么!

更新 2

Filter.Eq的定义如下:

 public FilterDefinition<TDocument> Eq<TField>(FieldDefinition<TDocument, TField> field, TField value);

【问题讨论】:

  • 这个错误是从哪里来的?是 Mongo 驱动程序还是您的代码,因为如果它是您的代码,那么它可能的修复并且会更复杂的是 Mongo 驱动程序导致它
  • 还有Filter.Eq的定义是什么,它期望什么样的对象,我想,它是一个对象类型,可以接受任何属性
  • 我假设这不起作用,因为驱动程序试图将其转换为 C# 以外的其他查询。使用GetType 当然是行不通的。这与 EntityFramework 相同。如果您在查询中有 .net 逻辑,则无法将其转换为 SQL(或其他),因此您会收到错误消息。这只是一个假设,但对我来说很有意义。 x =&gt; x.Bla 之所以有效,是因为 x 代表一个表,而 Bla 代表一个列,因此它可以轻松地将其转换为用于查询的任何内容。
  • 试试x =&gt; x.GetType().GetProperty(PropertyName).GetValue(x),因为它期望比较的值不仅仅是PropertyInfo
  • 在我的脑海中,Mongo 驱动程序使用反射从 MemberExpression 获取属性名称。 x =&gt; x.GetType().GetProperty(PropertyName) 不是 MemberExpression,所以这是行不通的。你想解决什么问题?

标签: c# mongodb generics lambda


【解决方案1】:

在代码中

FIlter.Eq(x =&gt; x.GetType().GetProperty(PropertyName), value),我的不足是 Mongo 驱动程序需要一个 Expression,它是在您使用元数据时自动创建的,如原始情况 x =&gt; x.AnyProperty

在这种情况下,您需要明确提供MemberExpression,如下所示

var parameterExpression = Expression.Parameter(typeof(T),"x");

var memberAccessExpression = Expression.MakeMemberAccess(parameterExpression, typeof(T).GetProperty("AnyProperty"));

现在您可以向FIlter.Eq 提供值memberAccessExpression,在这种情况下,如果AnyProperty 不是type T 的一部分,它将在运行时失败,因为它在运行时经过验证。

ExpressionTrees,这是x =&gt; x.AnyProperty的替换

编辑 1:

查看Mongo DB Driver文档,以下是Definitions and Builders的重要细节,示例如下:

var builder = Builders<Widget>.Filter;
var filter = builder.Eq(widget => widget.X, 10) & builder.Lt(widget => widget.Y, 20);

以下是FilterDefinitionBuilder.Eq 的定义,它公开了Eq 和其他各种过滤器:

public FilterDefinition<TDocument> Eq<TField>(Expression<Func<TDocument, TField>> field,TField value)

在这种情况下,我们需要一个泛型类型TDocument,它是主类和TField,它是应用过滤器的字段的类型,因此您的代码是:

var builder = Builders<T>.Filter;

// Use makeMemberAccessExpression created above
var filter = builder.Eq(Expression.Lambda<Func<T,string>>(makeMemberAccessExpression), "<stringValue>");

要点:

  1. 从 Mongo 文档中可以看出,我们有 2 种泛型类型,TDocumentTField,在这里您只使用一种,因此对于上面的代码,它将被限制为字符串作为 @ 的类型987654342@,除非您将其设为通用,这是您的选择,否则您的所有字段都应为您提供的特定类型,在这种情况下为字符串
  2. 更重要的是值应为您提供的 TField 类型,否则它将不起作用并在编译时失败
  3. 还有另一个重载 Eq&lt;TField&gt;(FieldDefinition&lt;TDocument, TField&gt;, TField),它的工作方式类似,但由于它需要 FieldDefinition&lt;TDocument, TField&gt;,我们需要提供 Expression&lt;Func&lt;TDocument,TField&gt;&gt; 作为类的一部分,这两个重载都转换为类似的代码

【讨论】:

  • Eq 接受FilterDefinion 作为第一个参数,所以我不能传递memberAccessExpression
  • 您将架构粘贴到问题更新中,我猜它需要MemberExpression,实际上您在实际代码中提供了 Lambda
  • FilterDefinition&lt;T&gt;你的自定义类
  • 现在你需要发布FieldDefinition&lt;TDocument, TField&gt;的定义,请减少到它有.Net代码的程度,而不是他们的自定义代码
  • 我需要另辟蹊径....挖得太深会让事情变得更复杂。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-24
相关资源
最近更新 更多