Aousaf Rashid
Aousaf Rashid

Reputation: 5748

Access property of a generic type inside lambda expression

The title may be ambiguous, but let me explain.

I am working with MongoDb and with the c# driver for MongoDb, we can create a FilterDefinition<T> to create a filter to fetch data as follows :

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

I am trying to put this code inside a reusable generic method, so that i don't end up writing the same code again and again. I won't be including the entire function here, but inside the function, i am trying to do something as follows :

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

Now the drawbacks are :

This results in an exception :

Unable to determine the serialization information for x => x.GetType().GetProperty("UserName"). // UserName is the property name

So, my question is, what can i do here for the generic type, which would be equivalent of x => x.PropertyName inside the lambda expression ?

Update

Forgot to mention, i did try this :

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

But it doesn't return the results from the database, where as this does :

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

I really wonder why!

Update 2

Definition of Filter.Eq is as follows :

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

Upvotes: 2

Views: 1397

Answers (1)

Mrinal Kamboj
Mrinal Kamboj

Reputation: 11478

In the code

FIlter.Eq(x => x.GetType().GetProperty(PropertyName), value), my undersatnding is that Mongo driver is expecting an Expression, which is automatically created when you use the Metadata like in original case x => x.AnyProperty

In this case you need to explicitly supply MemberExpressionas follows

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

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

Now you can supply to the FIlter.Eq value memberAccessExpression, in this case it will fail at run-time, if the AnyProperty is not part of type T, since its verified at the run-time.

In ExpressionTrees, this is the replacement of the x => x.AnyProperty

Edit 1:

Reviewing the Mongo DB Driver documents, following are the important details on the Definitions and Builders, there's an example as follows:

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

Following is the definition of the FilterDefinitionBuilder.Eq, which expose the Eq and various other filters:

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

In this case, we need a generic type TDocument, which is the main class and TField, which is the type of the field on which filter is applied, therefore code in your case would be:

var builder = Builders<T>.Filter;

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

Important points:

  1. As it can be seen in the Mongo documentation, we have 2 generic types, TDocument and TField, here you are working with just one, so with the above code it will be restricted to string as type for TField, until and unless you make that also generic, which is your choice, otherwise all your fields shall be of specific type that you supply, which is string in this case
  2. More important is the value shall be of type TField that you supply else it will not work and will fail at compile time
  3. There's another overload, Eq<TField>(FieldDefinition<TDocument, TField>, TField), which would work in a similar way, but since it expects FieldDefinition<TDocument, TField>, we need to supply the Expression<Func<TDocument,TField>> as part of the class, both overloads translates into similar code

Upvotes: 3

Related Questions