I have the following scenario:
I have an unknown at compile time DbSet, which I get it via its type like:
DbSet entities = _repository.Context.Set(myType)
I have a dynamically-built expression of a given type,
Expression myFilter; //built as an expression of myType, constructed at runtime
How can I apply myFilter on entities, in order to filter out the entities based on myFilter?
below is a code that might help you: it finally creates a IQueryable of myType that actually represents something like
SELECT * FROM YourMappedTable WHERE Id = 1 but of cource, instead of using my expression that i have built for demo purpose, you could use your expression.
class Program
{
static void Main(string[] args)
{
using (var x = new DB01Entities ())
{
Type myType = typeof(Angajati);
var setMethod = typeof(DB01Entities).GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public).Where (a => a.Name == "Set" && a.IsGenericMethod).First ().GetGenericMethodDefinition ();
var mySet = setMethod.MakeGenericMethod(myType);
var realSet = mySet.Invoke(x, null);
var param1 = Expression.Parameter(myType, "param1");
var propertyExpresion = Expression.Property(param1, "Id");
var idExpresssion = Expression.Constant(1);
var body = Expression.Equal(propertyExpresion, idExpresssion);
var lambda = Expression.Lambda(body, param1);
var genericTypeCaster = typeof(Program).GetMethod("Caster", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetGenericMethodDefinition();
var effectiveMethod = genericTypeCaster.MakeGenericMethod(myType);
var filteredQueryable = effectiveMethod.Invoke(null, new Object[] {realSet, lambda });
}
}
private static IQueryable<T> Caster <T> (DbSet<T> theSet, Expression whereCondition) where T : class
{
return theSet.Where(whereCondition as Expression<Func<T, bool>>);
}
}
So "lambda" variable above is the equivalent for your "myFilter". It must be at runtime an
Expression<Func<YourType, bool>>.
And mySet is your "entities' DbSet.
Happy coding!
Related
I have an IQueryable<T> from my DbSet in Entity Framework. I am provided a "Fuzzy Search String", named searchText, like so:
public List<T> Search<T>(string searchText)
{
using (var context = ...)
{
var baseQuery = context.Set<T>().AsQueryable();
baseQuery = baseQuery.Where(x =>
DbFunctions.Like(x.PropertyName, searchText)
|| DbFunctions.Like(x.PropertyTwo, searchText)
|| DbFunctions.Like(x.PropertyThree, searchText)
|| DbFunctio..... etc
);
return baseQuery.ToList();
}
}
But given the generic nature, I don't know what properties there are on the type. I can provide an abstract method to somebody implementing this which allows them to give me a List of Properties (or even PropertyInfo or whatever else, I can figure that out). But I don't know how to dynamically create the expression. This is what I have so far:
var baseQuery = context.Set<T>().AsQueryable();
var expression = baseQuery.Expression;
var colName = "colName"; // Or names, I can iterate.
var parameter = Expression.Parameter(typeof(T), "x");
var selector = Expression.PropertyOrField(parameter, colName);
expression = Expression.Call(typeof(DbFunctions), nameof(DbFunctions.Like),
new Type[] { baseQuery.ElementType, selector.Type },
expression, Expression.Quote(Expression.Lambda(selector, parameter)));
The problem here is... well, it doesn't work to begin with. But mainly that I'm not using the searchText anywhere in it, and don't know how to plug it in. I THINK I'm close... but have spent an inordinate amount of time on it.
Hopefully I'm getting your query logic right: if you want to build a set of LIKE conditions based on known type and list of column names, you could try something like this:
static private MethodInfo dbLikeMethod = typeof(DbFunctions).GetMethod(nameof(DbFunctions.Like), BindingFlags.Public | BindingFlags.Static, null, new Type[] {typeof(string), typeof(string)}, null); // I am targeting DbFunctions.Like(string, string). You might want another overload (or even mix them up depending on your inputs)
public List<T> Search<T>(string searchText) where T: class
{
using (var context = new ...)
{
var baseQuery = context.Set<T>().AsQueryable().Where(CreateExpression<T>(searchText));// you could probably find a more elegant way of plugging it into your query
return baseQuery.ToList();
}
}
Expression<Func<T, bool>> CreateExpression<T>(string searchText) where T : class
{
var cols = new List<string> {
"PropertyName",
"PropertyTwo" // i understand you've got a way to figure out which strings you need here
};
var parameter = Expression.Parameter(typeof(T), "x");
var dbLikeCalls = cols.Select(colName => Expression.Call(dbLikeMethod, Expression.PropertyOrField(parameter, colName), Expression.Constant(searchText))); // for convenience, generate list of DbFunctions.Like(x.<Property>, searchText) expressions here
var aggregatedCalls = dbLikeCalls.Skip(1).Aggregate((Expression)dbLikeCalls.First(), (accumulate, call) => Expression.OrElse(accumulate, call)); // aggregate the list using || operators: use first item as a seed and keep adding onto it
return Expression.Lambda<Func<T, bool>>(aggregatedCalls, parameter);
}
I need to create a table joins at the runtime with a given configuration.
In this case, I have the IQueryable property which is the root and I need to create the join dynamically.
Here what I have tried out.
public class MyDbContext : DbContext
{
public IQueryable<T> AsDynamicQueryable<T>() where T : class
{
var predicate = default(Func<T, bool>); // This is a Dynamically generated predicate
var query = this.Set<T>().Where(predicate).AsQueryable();
// Now here I need to append a JOIN to the above 'query'
// So far, this is what I have done.
var rootType = typeof(T);
var innerType = Type.GetType("This type takes from the configuration");
var innerExpression = this.Set(innerType).AsQueryable();
var paramOne = Expression.Parameter(rootType, "p1");
var paramTwo = Expression.Parameter(innerType, "p2");
var outerKeySelector = Expression.Property(paramOne, "property_one"); //'property_one' is a property of a first parameter which takes from the configuration
var outerKeySelectorExpression = Expression.Lambda(outerKeySelector, paramOne); // (p1)=>p1.property_one
var innerKeySelector = Expression.Property(paramTwo, "property_two"); //'property_two' is a property of a 2nd parameter which takes from the configuration
var innerKeySelectorExpression = Expression.Lambda(innerKeySelector, paramTwo); // (p2)=>p2.property_two
var resultSelector = Expression.Lambda(paramOne, paramOne, paramTwo); // (p1,p2)=>p1
var joinMethod = typeof(Queryable)
.GetMethods()
.First(m => m.Name == "Join" && m.GetParameters().Length == 5)
.MakeGenericMethod(rootType, innerType, typeof(int), rootType);
// 1st Apptempt.
// I'm not sure that I can execute the JOIN method like this.
// But anyway, this gives the below error when I try to execute via taking Count();
// "This method supports the LINQ to Entities infrastructure and is not intended to be used directly from your code."
var newQuery = (IQueryable<T>)joinMethod
.Invoke(
query,
new object[]
{
query,
innerExpression,
outerKeySelectorExpression,
innerKeySelectorExpression,
resultSelector
});
var tt = newQuery.Count(); // Here I just try to execute the expression to check whether it works before I return the Queryable.
// 2nd Attempt
// This also gives the following error when I try to execute via taking Count();
// Unable to create a constant value of type '<type name of the root(T) type>'. Only primitive types or enumeration types are supported in this context.
var joinMethodCallExpression = Expression.Call(
null,
joinMethod,
query.Expression,
innerExpression.Expression,
outerKeySelectorExpression,
innerKeySelectorExpression,
resultSelector);
var xx = this.Set<T>().AsQueryable().Provider.CreateQuery<T>(joinMethodCallExpression);
var te = xx.Count(); // Here I just try to execute the expression to check whether it works before I return the Queryable.
throw new NotImplementedException();
}
}
Highly appreciate if someone can point out the correct way of doing this.
Here is the code. I've added my comments inside the code:
public IQueryable<T> AsDynamicQueryable<T>() where T : class
{
// ERROR!!! It should be Expression<Func<T, bool>>
// GetPredicate<T>() is my method to get the predicate. You must
// put here yours. IT must return an Expression<Func<T, bool>>
Expression<Func<T, bool>> predicate = GetPredicate<T>(); // This is a Dynamically generated predicate
// ERROR!!! Don't EVER use AsQueryable(), unless you exactly know
// what you are doing. In this example, your use of AsQueryable<>()
// is hiding the fact that you are executing the Where() LOCALLY,
// because it is a Where(this IEnumerable<>, Func<>) instead of
// being a Where(this IQueryable<>, Expression<>)
// If you want an IQueryable<>, put it in a IQueryable<> variable
IQueryable<T> query = this.Set<T>().Where(predicate);
var rootType = typeof(T);
var innerType = GetAsDynamicQueryableInnerType<T>();
// Same as before! Don't use .AsQueryable(). In this case, use
// IQueryable (non-generic). Note that in this case there was
// no problem with yoru code, so AsQueryable() wasn't doing
// "damage"
IQueryable innerExpression = this.Set(innerType);
var paramOne = Expression.Parameter(rootType, "p1");
var paramTwo = Expression.Parameter(innerType, "p2");
// GetPrimaryKey() is my method to get the property to use.
// it returns a string with the name of the property
string primaryKeyRootType = GetPrimaryKey(rootType);
var outerKeySelector = Expression.Property(paramOne, primaryKeyRootType); //'property_one' is a property of a first parameter which takes from the configuration
var outerKeySelectorExpression = Expression.Lambda(outerKeySelector, paramOne); // (p1)=>p1.property_one
// GetForeignKey() is my method to get the property to use.
// it returns a string with the name of the property
var foreignKeyInnerType = GetForeignKey(innerType, rootType);
var innerKeySelector = Expression.Property(paramTwo, foreignKeyInnerType); //'property_two' is a property of a 2nd parameter which takes from the configuration
var innerKeySelectorExpression = Expression.Lambda(innerKeySelector, paramTwo); // (p2)=>p2.property_two
var resultSelector = Expression.Lambda(paramOne, paramOne, paramTwo); // (p1,p2)=>p1
// Using outerKeySelector.Type as the type of the third parameter
// here. 99% it is typeof(int), but why not make it more generic?
var joinMethod = typeof(Queryable)
.GetMethods()
.First(m => m.Name == "Join" && m.GetParameters().Length == 5)
.MakeGenericMethod(rootType, innerType, outerKeySelector.Type, rootType);
// Queryable.Join is static, so the first parameter must be null!
// Then the parameters to pass to Queryable.Join are the ones you
// where using in the 1st case.
var newQuery = (IQueryable<T>)joinMethod.Invoke(
null,
new object[]
{
query,
innerExpression,
outerKeySelectorExpression,
innerKeySelectorExpression,
resultSelector
});
return newQuery;
}
And then there is the big problem: even if it can work, you can only get back a IQueryable<T>, but the result of a Join is normally a IQueryable<T+U>. I see that you wrote resultSelector = ... (p1,p2)=>p1, but is it really what you want?
I'm creating a method that receives a Queryable<T> source, a string with a property name/path (could be a deep property for example "TrParent.DataTypes" to achieve this x => x.TrParent.DataTypes) and Enumerable<int> which holds the values I need to intersect.
Basically I come from the need to create the following query dynamically (I mean <DT_Det_Tr> and TrParent.DataTypes being know only at runtime, in the example DT_Det_Tr is not a type it is a class):
var _vals = new List<int>();
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any()
);
Please keep in mind that the preceding query is just an example of what I need to achieve dynamically, what I really need is an expression tree that creates a predicate like the one shown above but using a dynamic type and with the deep navigation property specified within a string.
So, I'm using this function to create the expression for the deep property:
private static LambdaExpression CreateDelegateExpression<T>(out Type resultingtype, string property, string parameterName = "x")
{
var type = typeof(T);
ParameterExpression param = Expression.Parameter(type, parameterName);
Expression expr = param;
foreach (string prop in property.Split('.'))
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, param);
resultingtype = type;
return lambda;
}
And here is what I have so far for my function:
public static IQueryable<T> Intersect<T>(this IQueryable<T> source, string property, IEnumerable<int> value)
{
//List of ids
var _value = Expression.Constant(value);
//Get delegate expression to the deep property and it's inner type
Type type = null;
var lambda = CreateDelegateExpression<T>(out type, property, "x");
var enumtype = type.GetGenericArguments()[0];
ParameterExpression tpe = Expression.Parameter(enumtype, "y");
Expression propExp = Expression.Property(tpe, enumtype.GetProperty("Id"));
MethodInfo innermethod = typeof(Queryable).GetMethods().Where(x => x.Name == "Select").First();
//Error on next line...
var selectCall = Expression.Call(typeof(Queryable),
"Select",
new Type[] { enumtype, typeof(long) },
lambda,
propExp);
//TODO: Add rest of logic and actually filter the source
return source;
}
In the var selectCall = line I'm getting error:
No generic method 'Select' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
I've read a lot here on SO and other sites but I can't get past this part, I feel I'm going to bump into more trouble when I get to the .Intersect(List<int>).Any() part so any help on that also would be grand, thanks.
After a lot of thought, investigation and attempts I came up with a solution.
First, I made a simpler version of my goal query (the static example I used in my question), so instead of:
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any()
);
I made this:
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Any(y => _vals.Contains(y.Id))
);
Which is a lot easier to translate to expressions (or at least it was for me) because it omits the Select call.
I got rid of the method I was using to create the deep navigation property expression and streamlined it in my Intersect function, this was because it was doing some work I don't really need here plus I needed access to some of the variables I use inside it, then I made this:
public static IQueryable<T> Intersect<T>(this IQueryable<T> source, string property, IEnumerable<int> value)
{
var type = typeof(T);
var _value = Expression.Constant(value); //List of ids
//Declare parameter for outer lambda
ParameterExpression param = Expression.Parameter(type, "x");
//Outer Lambda
Expression expr = param;
foreach (string prop in property.Split('.')) //Dig for deep property
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
//Get deep property's type
var enumtype = type.GetGenericArguments()[0];
//Declare parameter for inner lambda
ParameterExpression tpe = Expression.Parameter(enumtype, "y");
//Inner Collection lambda logic
//Property for inner lambda
Expression propExp = Expression.Property(tpe, enumtype.GetProperty("Id"));
//Contains method call .Contains(y.Id)
var containsMethodExp = Expression.Call(typeof(Enumerable), "Contains", new[] { propExp.Type }, _value, propExp);
//Create Expression<Func<enumtype, bool>>
var innerDelegateType = typeof(Func<,>).MakeGenericType(enumtype, typeof(bool));
//Create Inner lambda y => _vals.Contains(y.Id)
var innerFunction = Expression.Lambda(innerDelegateType, containsMethodExp, tpe);
//Get Any method info
var anyMethod = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(enumtype);
//Call Any with inner function .Any(y => _vals.Contains(y.Id))
var outerFunction = Expression.Call(anyMethod, expr, innerFunction);
//Call Where
MethodCallExpression whereCallExpression = Expression.Call
(
typeof(Queryable),
"Where",
new Type[] { source.ElementType },
source.Expression,
Expression.Lambda<Func<T, bool>>(outerFunction, new ParameterExpression[] { param })
);
//Create and return query
return source.Provider.CreateQuery<T>(whereCallExpression);
}
I hope this helps anyone trying to develop a similar solution.
Working with expression trees can be very hard and frustrating at first, but it's a really powerful tool once you get the hold of it.
If you have access to the dynamic keyword from c# 4.0, you might be able to work around the problem like this:
var _vals = new List<int>();
var res = dbContext.Set<DT_Det_Tr>()
.Where(obj => { dynamic x = obj;
return x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any();
}
);
But I don't know enough about the details of the problem you want to solve to say for sure.
Background:
I have a web service that returns the rows in a table (table name supplied as a parameter) with Ids greater than a certain Id (also supplied as a parameter). We are assuming the Ids are sequential.
I am using Linq to SQL for database interaction so I want to return the new rows as:
List<WhateverObject>
Because we only know the table name at runtime, I can't use Linq in a normal fashion which has made things much more complex.
Question:
The code is below (and it works). How can I simplify it? It seems overly complex.
private object GetUpdateList(string tableName, int Id, DataClassesDataContext db)
{
PropertyInfo pi = db.GetType().GetProperty(tableName);
var table = pi.GetValue(db, null);
// Get type of object within the table.
Type genericType = table.GetType().GetGenericArguments()[0];
// The Where method lives on the Enumerable type in System.Linq
var whereMethods = typeof(System.Linq.Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => mi.Name == "Where");
// There are actually 2 where methods - we want the one with 2 parameters
MethodInfo whereMethod = null;
foreach (var methodInfo in whereMethods)
{
var paramType = methodInfo.GetParameters()[1].ParameterType;
if (paramType.GetGenericArguments().Count() == 2)
{
// we are looking for Func<TSource, bool>, the other has 3
whereMethod = methodInfo;
break;
}
}
Func<object, bool> IdEquals = BuildEqFuncFor("Id", Id);
whereMethod = whereMethod.MakeGenericMethod(genericType);
var result = whereMethod.Invoke(table, new object[] { table, IdEquals });
MethodInfo toListMethod = typeof(System.Linq.Enumerable).GetMethod("ToList").MakeGenericMethod(genericType);
return toListMethod.Invoke(result, new object[] { result });
}
// Build lambda expression for use in Linq
private static Func<object, bool> BuildEqFuncFor(string prop, object val)
{
// We know we are comparing integers here so cast them.
// There is probably a more general solution.
return t => (int)t.GetType().InvokeMember(prop, BindingFlags.GetProperty, null, t, null) > (int)val;
}
To come up with this solution I have had to reference the following questions:
How do I invoke an extension method using reflection?
How to call extension method "ElementAt"of List<T> with reflection?
How to get the type of T from a member of a generic class or method?
Convert func to predicate using reflection in C#
Try something like this:
private IList GetUpdateList(string tableName, int id, DataClassesDataContext db)
{
System.Reflection.PropertyInfo pi = db.GetType().GetProperty(tableName);
var table = pi.GetValue(db, null);
// Get type of object within the table.
Type genericType = table.GetType().GetGenericArguments()[0];
var param = Expression.Parameter(genericType, "x");
var predicateExpr = Expression.Lambda(
Expression.GreaterThan(
Expression.Property(param, "Id"),
Expression.Constant(id)),
param);
return this
.GetType()
.GetMethod("GetUpdateListGeneric")
.MakeGenericMethod(genericType)
.Invoke(this, new[] { table, predicateExpr }) as IList;
}
private IList<T> GetUpdateListGeneric<T>(
Table<T> table,
Expression<Func<T, bool>> predicate) where T : class
{
return table.Where(predicate).ToList();
}
How would you translate the following generic Lambda function into a lambda expression :
context.AssociateWith<Product>(p => p.Regions.Where(r => r.Country == 'Canada')
I'm trying to create a full lambda expression without any <T> or direct call. Something like :
void AddFilter(ITable table, MetaDataMember relation)
{
var tableParam = Expression.Parameter(table.ElementType, "e");
var prop = Expression.Property(tableParam, relation.Name);
var func = typeof(Func<,>).MakeGenericType(table.ElementType, relation.type)
var exp = Expression.Lambda(func, prop, tableParam);
}
This will produce e.Regions... but I'm unable to get the Where part from there...
I know I'm very late in the game with my answer and likely this is not the exact solution you are looking for (still uses the frequently), but maybe it will help you and others building their expression:
/*
example: Session.Query.Where(m => m.Regions.Where(f => f.Name.Equals("test")))
*/
var innerItem = Expression.Parameter(typeof(MyInnerClass), "f");
var innerProperty = Expression.Property(innerItem, "Name");
var innerMethod = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var innerSearchExpression = Expression.Constant(searchString, typeof(string));
var innerMethodExpression = Expression.Call(innerProperty, innerMethod, new[] { innerSearchExpression });
var innerLambda = Expression.Lambda<Func<MyInnerClass, bool>>(innerMethodExpression, innerItem);
var outerItem = Expression.Parameter(typeof(MyOuterClass), "m");
var outerProperty = Expression.Property(outerItem, info.Name);
/* calling a method extension defined in Enumerable */
var outerMethodExpression = Expression.Call(typeof(Enumerable), "Where", new[] { typeof(MyInnerClass) }, outerProperty, innerLambda);
var outerLambda = Expression.Lambda<Func<MyOuterClass, bool>>(outerMethodExpression, outerItem);
query = query.Where(outerLambda);
Based on an answer posted here: Creating a Linq expression dynamically containing a subquery.
Try this, it's not pretty but it gives you a valid expression for the whole structure. You could define the inner lambda as an expression but you would still have to compile it before you could pass it to Where(), so for the purposes of this answer it seems redundant.
Expression<Func<Product, IEnumerable<Region>>> getRegions =
p => p.Regions.Where(r => r.Country == "Canada");