lambda表达式内的通用类型的访问属性

时间:2019-08-19 08:57:18

标签: c# mongodb generics lambda

标题可能不明确,但让我解释一下。

我正在使用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 => x.GetType().GetProperty(PropertyName), value)

这会导致异常:

  

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

所以,我的问题是,对于泛型类型,它在lambda表达式中相当于x => 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);

1 个答案:

答案 0 :(得分:2)

  

在代码中

FIlter.Eq(x => x.GetType().GetProperty(PropertyName), value),我的不足是Mongo驱动程序期望一个Expression,当您像原始情况下的x => x.AnyProperty

一样使用元数据时,它会自动创建。
  

在这种情况下,您需要按以下方式明确提供MemberExpression

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

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

现在,您可以提供FIlter.EqmemberAccessExpression,在这种情况下,如果AnyProperty不属于type T,它将在运行时失败。在运行时对其进行了验证。

ExpressionTrees中,这是x => x.AnyProperty

的替换
  

编辑1:

查看Mongo DB驱动程序文档,以下是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,在这里您只使用一种,因此使用上面的代码,它将限于字符串作为TField的类型,直到并且除非您也将其设为通用(这是您的选择),否则所有字段均应为您提供的特定类型,在这种情况下为字符串
  2. 更重要的是,您提供的值应为TField类型,否则它将不起作用并且在编译时将失败
  3. 还有另一个重载Eq<TField>(FieldDefinition<TDocument, TField>, TField),其工作方式类似,但是由于它期望FieldDefinition<TDocument, TField>,因此我们需要提供Expression<Func<TDocument,TField>>作为类的一部分,这两个重载都将转换为变成类似的代码