0

I want to write a query in .net core using ef. Now the issue is, I am getting the property name in parameter and I want to put condition on that property i.e

  public IQueryable<MyModel> Generate(string property, IQueryable<MyModel> query)
  {
    query = query.Where(a => a.property.ToLower() != null);
  }

MyModel:

public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }

Method Calling:

var query = Generate(MyModel.Property2, query);

I know this is possible using reflection but that has some performance impacts. So is there any better approach?

4
  • I don't quite understand what you want to achieve. Commented Oct 9, 2018 at 7:26
  • Also this will not work property.ToLower() != null, ToLower() never returns null. Commented Oct 9, 2018 at 7:27
  • Are you passing MyModel.Property2 or nameof(MyModel.Property2)? MyModel.Property2 does not contain property name, it contain property value... Commented Oct 9, 2018 at 7:28
  • Simply, no. You must use reflection. That is the only way to take a string and somehow access a real property via that. There's ways you can encapsulate the logic, as some of the answers below do, but ultimately, you're still using reflection. Commented Oct 9, 2018 at 12:59

4 Answers 4

3

Can you use Func<MyModel, string> as param?

public IQueryable<MyModel> Generate(Func<MyModel, string> propertySelector, IQueryable<MyModel> query)
{
   query = query.Where(a => propertySelctor(a).ToLower() != null);
}

And use like:

var query = Generate(m => m.Property2, query);
Sign up to request clarification or add additional context in comments.

2 Comments

Whats the idea behind lowering string's case and checking if it's not null?
I think, I wrote the question wrongly. The issue is I don't exactly which property I need to pass in Generate(m => m.Property2, query); I have only string value that I can pass. It might be Property1 or Property2 or Property3. Like Generate("property3", query);
3

EDIT after question clarification:

You could implement a generic extension method for IQueryable where the clause is dynamically built:

public static class QueryExtensions
{
    public static IQueryable<TModel> WhereNotNull<TModel>(this IQueryable<TModel> query, string propertyName)
    {
        var parameter = Expression.Parameter(typeof(TModel), "x");
        var body = Expression.PropertyOrField(parameter, propertyName);
        var comparison = Expression.NotEqual(body, Expression.Constant(null, typeof(object)));
        var lambda = Expression.Lambda<Func<TModel, bool>>(comparison, parameter);
        return query.Where(lambda);
    }
}

Call it like:

query = query.WhereNotNull(nameof(MyModel.Property1));

or

query = query.WhereNotNull("Property1");

EDIT after request for null or whitespace check on strings:

Example of calling string.IsNullOrWhiteSpace() on string properties:

public static IQueryable<TModel> WhereNotNull<TModel>(this IQueryable<TModel> query, string propertyName)
{
    var parameter = Expression.Parameter(typeof(TModel), "x");
    var body = Expression.PropertyOrField(parameter, propertyName);
    Expression<Func<TModel, bool>> lambda = null;
    if (body.Type == typeof(string))
    {
        var methodCall = Expression.Call(typeof(string), nameof(string.IsNullOrWhiteSpace), null, body);
        var nullOrWhiteSpaceComparison = Expression.Not(methodCall);
        lambda = Expression.Lambda<Func<TModel, bool>>(nullOrWhiteSpaceComparison, parameter);
    }
    else
    {
        var nullComparison = Expression.NotEqual(body, Expression.Constant(null, typeof(object)));
        lambda = Expression.Lambda<Func<TModel, bool>>(nullComparison, parameter);
    }
    return query.Where(lambda);
    }

If you (in some other context) want to combine multiple expressions you may use Expression.And(Expression left, Expression right) or Expression.Or(Expression left, Expression right). Pass the unary/binary expression as parameters left and right.

5 Comments

The issue is I don't exactly which property I need to pass in Generate(m => m.Property2, query); I have only string value that I can pass. It might be Property1 or Property2 or Property3. Like Generate("property3", query)
If you are using C# 6 there is no need to create custom extension for null checking. See this question about more info.
Ask, I have updated my answer to support dynamically building the where clause based on the property name.
Thanks alot, what if I have to add 2 conditions. Let say null or whitespace check?
I updated the answer. You don't really need to combine two conditions, just call the string.IsNullOrWhiteSpace() method.
0

You can do it like this:

public IQueryable<TModel> Generate<TModel, TProperty>(Func<TModel, TProperty> propertyFunc, IQueryable<TModel> query)
{
    return query.Where(a => propertyFunc(a) != null);
}

It will be a more flexible solution than Dmitry.

1 Comment

I think, I wrote the question wrongly. The issue is I don't exactly which property I need to pass in Generate(m => m.Property2, query); I have only string value that I can pass. It might be Property1 or Property2 or Property3. Like Generate("property3", query);
0
public IQueryable<MyModel> Generate(string property, IQueryable<MyModel> query)
{
    query = query.Where(a => EF.Property<string>(a, property) != null);
}

Reference: EF.Property(Object, String) Method

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.