I am creating an IQueryable that I want to use for a query passed to entity framework. My repository does not expose queryable.
var query = new List<Entity>().AsQueryable().Where(x => x.Property == "argument");
I have a method on my repository that will take in an IQueryable.
How do I query my DbSet with the same queryable? I am trying to extract the expression from the queryable to build a new expression for the dbset. Here is what I have so far but it does not work:
public IDbSet<TEntity> DbSet { get; set; }
public IEnumerable<TEntity> Find(IQueryable<TEntity> queryable)
{
var parameter = Expression.Parameter(typeof (TEntity));
var body = queryable.Expression;
var lambda = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
var result = DbSet.Where(lambda);
return null;
}
The code fails when I try and create the lambda with the following error:
Expression of type 'System.Linq.IQueryable`1[MyTEntity]' cannot be used for return type 'System.Boolean'
I'm clearly not building the expression correctly, what am I missing? Is there an easier way to do what I'm trying to accomplish?
Also I've seen some examples that show an Expression should have a parameters property. But no matter what type of expression type I cast to, and this one is ConstantExpression, I don't see a parameters property from the IQueryable.Expression.
In your case, queryable.Expression represents the whole expression Queryable.Where(constantList, x => x.Property == "argument"). If you want just the Where() lambda, you need to extract it. To do that, you could use code like this:
public IEnumerable<TEntity> Find<TEntity>(IQueryable<TEntity> queryable)
{
var methodCall = queryable.Expression as MethodCallExpression;
Func<IQueryable<TEntity>, Expression<Func<TEntity,Boolean>>, IQueryable<TEntity>> whereDelegate = Queryable.Where;
if (methodCall.Method == whereDelegate.Method
&& methodCall.Arguments[0] is ConstantExpression)
{
var whereLambdaQuote = (UnaryExpression)methodCall.Arguments[1];
var whereLambda = (Expression<Func<TEntity, bool>>)whereLambdaQuote.Operand;
var result = DbSet.Where(whereLambda);
}
return null;
}
Related
I have a generic function GetDocuments<T> that's querying the CosmosDB API. The generic is constrained by a custom IDocument interface. At the moment, I'm passing an enum as an argument to this function that determines the type of the document -- however, my interface has the document type as a property, so it seems like I should be able to access that somehow instead of passing another arg.
Because my argument is in an Expression, I'm not sure how to access that value (I'm not sure if using the API to access expression params is the right approach). If I had an IDocument as an argument, it seems pretty straightforward to access it.
Given this code, how can I access the DocumentType without passing it to GetDocuments<T>?
Function definition:
public IEnumerable<T> GetDocuments<T>(Expression<Func<T, bool>> predicate, Enumerations.DocumentType type) where T : IDocument
{
var results = Client.CreateDocumentQuery<T>(GetDocumentCollectionUri(), GetFeedOptions())
.Where(predicate)
.Where(s => s.DocumentType == type)
.ToList();
return results;
}
Interface definition:
public interface IDocument
{
[JsonProperty(PropertyName = "id")]
string Id { get; set; }
[JsonProperty(PropertyName = "documentType")]
Enumerations.DocumentType DocumentType { get; }
}
Function call:
var messages = mailboxRepository.GetDocuments<MailboxMessageTemplate>(
s => s.UserId == user.ID,
Enumerations.DocumentType.MessageTemplate);
You can do that by pre-creating your expression and just adding it to your query.
Here is the expression that would do the trick.
internal static Expression<Func<T, bool>> TypeSpecificExpression<T>() where T : class
{
var parameter = Expression.Parameter(typeof(IDocument));
var member = Expression.Property(parameter, nameof(IDocument.Enumerations.DocumentType));
var contant = Expression.Constant(nameof(T));
var body = Expression.Equal(member, contant);
var extra = Expression.Lambda<Func<T, bool>>(body, parameter);
return extra;
}
You can then simple change your method to be:
public IEnumerable<T> GetDocuments<T>(Expression<Func<T, bool>> predicate) where T : IDocument
{
var results = Client.CreateDocumentQuery<T>(GetDocumentCollectionUri(), GetFeedOptions())
.Where(predicate && TypeSpecificExpression())
.ToList();
return results;
}
Obviously I don't have access to the Enumerations.DocumentType enum so you might need to do some tweeting on the value you are setting here: var contant = Expression.Constant(nameof(T));
On a side note, you should not be calling .ToList() like that on CreateDocumentQuery. You are synchornizing a query that can be a serious performance hit. You should be using the .AsDocumentQuery() method to get the query and then call query.ExecuteNextAsync when query.HasMoreResults.
On a second side note, it looks like you are trying to build something that the library Cosmonaut already does, including the feature you just asked a question for (you can find that method here). It's worth taking a look.
Disclaimer: I made Cosmonaut
So you are looking for a way to convert an Expression<Func<T, bool>> to a Func<T, bool>?
You can call Compile.
Compiles the lambda expression described by the expression tree into executable code and produces a delegate that represents the lambda expression.
var results = Client.CreateDocumentQuery<T>(GetDocumentCollectionUri(), GetFeedOptions())
.Where(predicate.Compile())
.Where(s => s.DocumentType == type)
.ToList();
The requirement is to pass any custom expression query to the documentDB and fetch records based on it.
public Task<dynamic> ExecuteQuery(Func<TEntity, dynamic> lambda)
{
dynamic result = client.CreateDocumentQuery<dynamic>(documentCollectionUri).Where(lambda); //compile error
return result;
}
above function can be called like this:
var res = await _locationDbRepository.ExecuteQuery(x => x.Name == "raja" && x.Address == "abc");
Clearly, it will give compile time error because Where() is expecting a predicate but, I need to pass a func. I don't want to pass predicate because it always return a boolean but func can return anything.
A SQL query can be passed to documentDB but then I have to convert lambda expression to SQL query using a 3rd party library which I don't want to use.
The above approach to query documentdb is not working so Any other way you can suggest based on lambda expression?
So, I figured out the solution by passing Expression<Func<TEntity, bool>> as parameter and it's returning expected results based on the lambda expression.
public async Task<IEnumerable<TEntity>> GetByExpression(Expression<Func<TEntity, bool>> expression)
{
IEnumerable<TEntity> IEnumerable;
List<TEntity> List = new List<TEntity>();
try
{
IDocumentQuery<TEntity> Queryable = client.CreateDocumentQuery<TEntity>(documentCollectionUri)
.Where(expression)
.AsDocumentQuery();
while (Queryable.HasMoreResults)
{
foreach (TEntity t in await Queryable.ExecuteNextAsync<TEntity>())
{
List.Add(t);
}
}
}
catch (DocumentClientException ex)
{
throw ex;
}
IEnumerable = List;
return IEnumerable;
}
It can be called like this:
dynamic results = SomeObj.GetByExpression(c=>c.Name== "raja" || c.Rank==12);
I have this function:
public List<T> Find(Expression<Func<T, bool>> query)
{
}
Find(x => x.Id == 4);
Inside the method Find I want to chain And Condition.
something like:
query.And(x => x.Secured == false);//Secured is a memeber inside T like Id.
Your problem is that you want to access a member of T within the generic method. T could be anything at this point so the compiler will not let you access Secured since T may not have a Secured member.
You could cast T to dynamic, but this just changes a compile time error to a runtime error (plus it's horrible).
The best way would be to ensure T implements some known interface that has a Secured member.
public List<T> Find(Expression<Func<T, bool>> query) where T : ISecured
The expression must be "opened" and rebuilt, like this:
public List<T> Find<T>(Expression<Func<T, bool>> query)
{
ParameterExpression parameter = query.Parameters[0];
Expression body = query.Body;
MemberExpression property = Expression.Property(parameter, "Secured");
body = Expression.AndAlso(body, Expression.Not(property));
Expression<Func<T, bool>> query2 = Expression.Lambda<Func<T, bool>>(body, parameter);
// Now you can use query2
return null;
}
Note that I'm considering this x.Secured == false to be equivalent to !x.Secured. Clearly Secured could be a strange class that overloads the == operator, but I'll ignore that case.
As suggested by #Ralf, you could even simply do two .Where. like:
public List<T> Find<T>(Expression<Func<T, bool>> query)
{
ParameterExpression parameter = query.Parameters[0];
MemberExpression property = Expression.Property(parameter, "Secured");
Expression<Func<T, bool>> query2 = Expression.Lambda<Func<T, bool>>(Expression.Not(property), parameter);
return context.Set<T>
.Where(query)
.Where(query2)
.ToList();
}
(I'm using as an example context.Set<T>, that is very similar to what you would do if you are using Entity Framework, but in general nearly all the IQuerable<>/IEnumerable<> treat two .Where() like a single .Where() with an && condition)
Something like
Find(x => x.Id == 4 && x.Secured == false);
I have an entity.
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
}
I want to create my own expression predicate. For that I have created a method that accepts property name and the value.
private static Expression<Func<Foo, bool>> Condition(string pName, object value)
{
var pe = Expression.Parameter(typeof(Foo), "foo");
var left = Expression.Property(pe, pName);
var right = Expression.Constant(value);
var equal = Expression.Equal(left, right);
var predicate = Expression.Lambda<Func<Foo, bool>>(equal, pe);
return predicate;
}
This is the predicate which works fine for a single condition.
using (var db = new MyEntities())
{
var predicate = Condition("Name", "foo");
var foos = db.Foos.Where(predicate).ToArray();
}
But when I tried to combine two conditions by following this post, it throws exception.
The parameter 'foo' was not bound in the specified LINQ to Entities
query expression.
using (var db = new MyEntities())
{
var cond1 = Condition("Name", "foo");
var cond2 = Condition("Code", "bar");
var body = Expression.AndAlso(cond1.Body, cond2.Body);
var predicate = Expression.Lambda<Func<Foo,bool>>(body, cond1.Parameters[0]);
var foos = db.Foos.Where(predicate).ToArray(); // exception
}
Please enlighten me.
The problem is that ParameterExpression in LINQ expressions is identified by reference equality, but the two Parameter objects are different references. (The name in ParameterExpression only exists for debugging purposes).
(If you reread the mentioned post, it says that the method that you tried would only work if both lambdas are defined on the same ParameterExpression object).
You have two big possibilities at this stage: either you define a way for the Condition function to accept a ParameterExpression object, or you create an ExpressionVisitor that will replace the original ParameterExpression with another. (Of course, given that you want to do an AndAlso, you could also conceivably chain two Where clauses, but that is less general.)
I have an expression like this
(a,b) => a.Id == b.Id
I would like to use it in LINQ to Entities query
T GetSingle(IRepository<T> repository, Func<T,T,bool> predicate, T entity)
{
return repository.GetAll().Single(e => predicate(e, entity))
}
but this results the exception: LINQ expression node type 'Invoke' is not supported in LINQ to Entities
As I understand I can use Expressions to construct a valide predicate for LINQ2SQL, so my expression
(a,b) => a.Id == b.Id and instance of entity with Id = 5 can result a new expression (a) => a.Id == 5.
And the last expression will be fine for LINQ to Entities.
I found and read this articles
Replace parameter in lambda expression
http://www.codeproject.com/Articles/143096/Parameter-Substitution-within-Expression-Trees
but still has no clue how to solve my task
So, how do I convert given expression dynamically?
Why don't you just change your method to be:
T GetSingle(IRepository<T> repository, Expression<Func<TSource, Boolean>> predicate)
{
return repository.GetAll().Single(predicate);
}
so instead of this:
GetSingle(myRepository, (a,b) => a.Id == b.Id, myEntity);
you should be able to do this:
GetSingle(myRepository, a => a.Id == myEntity.Id);
I haven't tested it with Linq2SQL, but it seems to me that you should be able to do this with an expression visitor and compiling the expression to write the value of your parameter into the expression you've supplied (assuming you switch over to using Expression<Func<T, T, bool>> instead of Func<T, T, bool>) and creating a wrapper that itself invokes Enumerable.Single on the result from the GetAll
The visitor (for specifically the example you've given would look like this)
public class VariableSubstitutionVisitor : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
private readonly ConstantExpression _constant;
public VariableSubstitutionVisitor(ParameterExpression parameter, ConstantExpression constant)
{
_parameter = parameter;
_constant = constant;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == _parameter)
{
return _constant;
}
return node;
}
}
Now, we'd adjust the GetSingle method to look like this:
public T GetSingle(IRepository<T> repository, Expression<Func<T, T, bool>> predicate, T entity)
{
//Create a new representation of predicate that will take just one parameter and capture entity
//Get just the body of the supplied expression
var body = predicate.Body;
//Make a new visitor to replace the second parameter with the supplied value
var substitutionVisitor = new VariableSubstitutionVisitor(predicate.Parameters[1], Expression.Constant(entity, typeof(T)));
//Create an expression that represents the predicate with the second parameter replaced with the supplied entity
var visitedBody = substitutionVisitor.Visit(body).Reduce();
//Make the new expression into something that could be a Func<T, bool>
var newBody = Expression.Lambda<Func<T, bool>>(visitedBody, predicate.Parameters[0]);
//Now, create something that will call Enumerable.Single on the result of GetAll from the repository, supplying the new predicate
//Make a place to hold the result of GetAll
var resultsParameter = Expression.Parameter(typeof (IEnumerable<T>));
//Make an expression that calls the Single extension method
var singleExpression = Expression.Call(((Func<IEnumerable<T>, Func<T, bool>, T>)Enumerable.Single).Method, resultsParameter, newBody);
//Make a Func<IEnumerable<T>, T> that return the result of the call of Single on the results of the GetAll method
var compiled = Expression.Lambda<Func<IEnumerable<T>, T>>(singleExpression, resultsParameter).Compile();
//Call GetAll, letting the new method that we've got run the supplied predicate without having to run an Invoke type expression
return compiled(repository.GetAll());
}
The trick, of course, is getting that to perform well.