How to build this complex Linq expression? - c#

I want to build a System.Linq.Expression from the string List like this:
System.Linq.Expressions.Expression x = null;
foreach (string s in GetWords(input))
{
/* Create Expression */
}
so I could use:
.Where(x =>
x.Name.Like(string.Format("%{0}%", word1)) ||
x.Name.Like(string.Format("%{0}%", word2)) ||
x.Name.Like(string.Format("%{0}%", word3)) ||
x.Id.ToString().Like(string.Format("%{0}%", word1)) ||
x.Id.ToString().Like(string.Format("%{0}%", word2)) ||
x.Id.ToString().Like(string.Format("%{0}%", word3)) ||
);
x is MyObject

Something like:
string[] words = { "foo", "bar", "blop" }; // your data
Expression body = null;
var param = Expression.Parameter(typeof(SomeType), "x");
var id = Expression.PropertyOrField(param, "Id");
var name = Expression.PropertyOrField(param, "Name");
foreach (string word in words)
{
var wordExpr = Expression.Constant(word, typeof(string));
var wordTest = Expression.OrElse(
Expression.Call(id, "Contains", null, wordExpr),
Expression.Call(name, "Contains", null, wordExpr));
body = body == null ? wordTest : Expression.OrElse(body, wordTest);
}
Expression<Func<SomeType,bool>>lambda;
if (body == null) { lambda = x => false; }
else { lambda = Expression.Lambda<Func<SomeType, bool>>(body, param); }

Related

Create expression trees that calls a method with nullable type

I would like to achieve this result by using expression trees:
x.DataMod == null ? string.Empty : x.DataMod.Value.ToShortDateString()
I'm trying to using Expression.Condition but i don't know how to call ToShortDateString().
I need the result in Linq GroupBy query.
var grouped = context.Receipt.GroupBy(x => (x.DataMod == null ? string.Empty : x.DataMod.Value.ToShortDateString()) + ";" + x.DataOraDocumento.ToShortDateString() + ";" + x.Cassa.Descrizione + ";" + x.Sezionale.Descrizione).ToList();
Thanks
As suggested in the comments, you should use Expression.Call to create an expression for ToShortDateString method invocation. I've composed a sample for I slightly simplified version of the original expression. See comments inline.
x.DataMod.HasValue ? x.DataMod.Value.ToShortDateString() : string.Empty
void Main()
{
var now = DateTime.UtcNow;
var tomorrow = now.AddDays(1);
var data = new Test[] {
new Test { DataMod = null },
new Test { DataMod = now },
new Test { DataMod = null },
new Test { DataMod = tomorrow },
new Test { DataMod = null },
new Test { DataMod = now }
};
var keySelector = BuildSelector();
var groups = data
.GroupBy(keySelector)
.ToList();
foreach (var group in groups)
{
Console.WriteLine($"Group = \"{group.Key}\"");
Console.WriteLine($"Items = {group.Count()}");
Console.WriteLine();
}
}
class Test
{
public DateTime? DataMod { get; set; }
}
Func<Test, string> BuildSelector()
{
// Builds delegate for the Expression:
// x => x.DataMod.HasValue ? x.DataMod.Value.ToShortDateString() : string.Empty
// Expression for: x
var x = Expression.Parameter(typeof(Test), "x");
// Expression for: x.DataMod.HasValue
var testExpr = Expression.Property(
Expression.Property(
x,
nameof(Test.DataMod)
),
nameof(Nullable<DateTime>.HasValue)
);
// Expression for: x.DataMod.Value.ToShortDateString()
var ifTrueExpr = Expression.Call(
// Expression for: x.DataMod.Value
Expression.Property(
Expression.Property(
x,
nameof(Test.DataMod)
),
nameof(Nullable<DateTime>.Value)
),
// Expression for: ToShortDateString
typeof(DateTime).GetMethod(
nameof(DateTime.ToShortDateString)
)
/* args if any */
);
// Expression for: string.Empty
var ifFalseExpr = Expression.Field(
null,
typeof(string).GetField(nameof(string.Empty))
);
// Expression for: x => x.DataMod.HasValue ? x.DataMod.Value.ToShortDateString() : string.Empty
var conditionExpr = Expression.Condition(
testExpr,
ifTrueExpr,
ifFalseExpr);
// Compile to a delegate
var lambda = Expression.Lambda<Func<Test, string>>(conditionExpr, x);
return lambda.Compile();
}

Want to make Func<UnknownType, bool> where UnknownType is only known at runtime

So, I'm experimenting with expressions atm. Got code below:
Code works fine except for 1 thing: I need to replace ViewModel type with ForeignKeyProperty.PropertyType, which is only known at runtime, at the line of var condition = Expression.Lambda < Func < ViewModel, bool> >
Expected end result:
ForeignKeyProperty.SetValue(model, repository.GetList <ForeignKeyProperty.PropertyType >().Single(x => x.Id == model.Id));
protected List < Action < IVenturaRepository, ViewModel>> SetForeignKeyProperties<ViewModel>() where ViewModel : BaseViewModel
{
var viewModelType = typeof(ViewModel);
var foreignKeyProperties = viewModelType.GetProperties().Where(x => x.PropertyType.IsSubclassOf(typeof(BaseViewModel)));
var actions = new List < Action < IVenturaRepository, ViewModel>>();
var repositoryType = typeof(IVenturaRepository);
foreach(var ForeignKeyProperty in foreignKeyProperties)
{
var foreignKeyIdProperty = viewModelType.GetProperties().SingleOrDefault(x => x.Name == ForeignKeyProperty.Name + "Id");
//ForeignKeyProperty.SetValue(model, repository.GetList<ViewModel>().Single(x => x.Id == model.Id));
var listMethod = repositoryType.GetMethods().SingleOrDefault(x => x.Name == "GetList").MakeGenericMethod(ForeignKeyProperty.PropertyType);
//Expression.Call(singleMethod,);
var repositoryVariable = Expression.Parameter(repositoryType, "repository");
var paramViewModelType = Expression.Parameter(viewModelType, "model");
var paramForeignEntityId = Expression.Property(paramViewModelType, "Id");
var listMethodCall = Expression.Call(repositoryVariable, listMethod);
var modelParameter = Expression.Parameter(ForeignKeyProperty.PropertyType, "x");
var foreignKeyTypeConstant = Expression.Constant(ForeignKeyProperty.PropertyType);
var condition =
Expression.Lambda < Func < ViewModel, bool>>(
Expression.Equal(
Expression.Property(paramViewModelType, foreignKeyIdProperty.Name),
Expression.Convert(Expression.Property(modelParameter, "Id"),foreignKeyIdProperty.PropertyType)
),
modelParameter
);
//var singleMethod = typeof(Enumerable).GetMethods().SingleOrDefault(x => x.Name.Equals("SingleOrDefault") && x.GetParameters().Count() ==2).MakeGenericMethod(viewModelType);
//var singleMethod = typeof(IEnumerable<ViewModel>).GetMethods().SingleOrDefault(x => x.GetParameters().Count() > 0).MakeGenericMethod(viewModelType);
//var singleLambda = Expression.Lambda(Expression.Property(modelParameter, "Id"), modelParameter);
var singleMethodCall = Expression.Call(typeof(Enumerable), "SingleOrDefault", new[] { ForeignKeyProperty.PropertyType },listMethodCall, condition);
//var singleMethodCall = Expression.Call(listMethodCall, singleMethod, condition);
var setMethod = ForeignKeyProperty.GetSetMethod();
var oParameter = Expression.Parameter(viewModelType, "obj");
var vParameter = Expression.Parameter(typeof(ViewModel),"value");
var method = Expression.Call(oParameter,setMethod, singleMethodCall);
var expression = Expression.Lambda<Action<IVenturaRepository, ViewModel>>(method);
actions.Add(expression.Compile());
}
return actions;
}
Could someone point me in the right direction please?
use Object as type. then you can check type by getType() at runtime and after check cast to correct type. or use dynamic to avoid casting.
So, I did as you suggested and used object instead. It works now.
Code below works in a specific controller that inherits from BaseController.
public override ActionResult Edit(long id, DeliveryEditViewModel model)
{
model.SourceModel.DeliveryRound = Repository.DeliveryRounds.Single(x => x.Id == model.DeliveryRoundId);
model.SourceModel.Sale = Repository.Sales.Single(x => x.Id == model.SaleId);
return base.Edit(id, model);
}
Idea is that code below works in BaseController:
public virtual ActionResult Edit(long id, EditModel model)
{
try
{
if (setForeignKeyActionList == null)
setForeignKeyActionList = SetForeignKeyProperties();
setForeignKeyActionList.ForEach(action => action(Repository, model.SourceModel));
Repository.SaveItem(model);
return RedirectToAction("Index");
}
catch(Exception ex)
{
ModelState.AddModelError("Error", ex);
return View(Repository.GetEditableItem(id));
}
}
This is the revised code based on your suggestion to use object type
protected List< Action< IVenturaRepository, ViewModel>> SetForeignKeyProperties()
{
var viewModelType = typeof(ViewModel);
var foreignKeyProperties = viewModelType.GetProperties().Where(x => x.PropertyType.IsSubclassOf(typeof(BaseViewModel)));
var actions = new List< Action< IVenturaRepository, ViewModel>>();
var repositoryType = typeof(IVenturaRepository);
foreach (var ForeignKeyProperty in foreignKeyProperties)
{
var foreignKeyIdProperty = viewModelType.GetProperties().SingleOrDefault(x => x.Name == ForeignKeyProperty.Name + "Id");
//ForeignKeyProperty.SetValue(model, repository.GetList< OtherViewModel>().Single(x => x.Id == model.Id));
var listMethod = repositoryType.GetMethods().SingleOrDefault(x => x.Name == "GetList").MakeGenericMethod(ForeignKeyProperty.PropertyType);
var repositoryVariable = Expression.Parameter(repositoryType, "repository");
var paramViewModelType = Expression.Parameter(viewModelType, "model");
var paramForeignEntityId = Expression.Property(paramViewModelType, "Id");
var listMethodCall = Expression.Call(repositoryVariable, listMethod);
var foreignKeyTypeConstant = Expression.Constant(ForeignKeyProperty.PropertyType);
var objectType = Expression.Parameter(typeof(object), "model");
var modelParameter = Expression.Parameter(typeof(object), "x");
var expressionForeignKeyId = Expression.Property(paramViewModelType, foreignKeyIdProperty.Name);
var expressionForeignEntityId = Expression.Convert(Expression.Property(Expression.Convert(modelParameter, ForeignKeyProperty.PropertyType), "Id"), foreignKeyIdProperty.PropertyType);
var condition =
Expression.Lambda<Func<object, bool>>(
Expression.Equal(
expressionForeignKeyId,
expressionForeignEntityId
),
modelParameter
);
var singleMethodCall = Expression.Call(typeof(Enumerable), "SingleOrDefault", new[] { ForeignKeyProperty.PropertyType }, listMethodCall, condition);
//var singleMethodCall = Expression.Call(listMethodCall, singleMethod, condition);
var setMethod = ForeignKeyProperty.GetSetMethod();
//var oParameter = Expression.Parameter(viewModelType, "obj");
var vParameter = Expression.Parameter(typeof(ViewModel), "value");
var method = Expression.Call(paramViewModelType, setMethod, singleMethodCall);
var lamdaParameterExpressions = new[]
{
repositoryVariable,
paramViewModelType
};
var expression = Expression.Lambda<Action<IVenturaRepository, ViewModel>>(method, lamdaParameterExpressions);
actions.Add(expression.Compile());
}
return actions;
}

How to convert Linq expression to Reflection (To make dynamic LINQ expression)

I have created three classes.
Two classes Data and IntArrayEqualityComparer are below-
public class Data
{
public Dictionary<int[], List<double>> s = new Dictionary<int[], List<double>>(new IntArrayEqualityComparer());
public Data()
{
}
public Data(Dictionary<int[], List<double>> s)
{
this.s = s;
}
public Dictionary<int[], List<double>> S
{
get { return s; }
set { s = value; }
}
}
public class IntArrayEqualityComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] x, int[] y)
{
if (x.Length != y.Length)
{
return false;
}
for (int i = 0; i < x.Length; i++)
{
if (x[i] != y[i])
{
return false;
}
}
return true;
}
public int GetHashCode(int[] obj)
{
int result = 17;
for (int i = 0; i < obj.Length; i++)
{
unchecked
{
result = result * 23 + obj[i];
}
}
return result;
}
}
A third class named Expression is created in which I need to convert LINQ expression into Reflection -
public class Expresion
{
public void CreateExpression()
{
Expression<Func<Data, List<int>>> exp1 = null;
//Below is the LINQ expression I want to convert
exp1 = p2 => p2.s[new int[] { 14, 5 }].Select((item, index) => new { item, index }).Select(x => x.index).ToList();
ParameterExpression p1 = Expression.Parameter(typeof(Data), "p");
MethodInfo mInfo = typeof(List<double>).GetMethod("get_Item");
MethodInfo mInfo1 = typeof(Dictionary<int, List<double>>).GetMethod("get_Item");
MethodInfo mInfo2 = typeof(Dictionary<int[], List<double>>).GetMethod("get_Item");
MethodInfo mInfo3 = typeof(List<int[]>).GetMethod("get_Item");
MemberExpression s1 = Expression.Property(p1, "s");
ParameterExpression index1 = Expression.Parameter(typeof(int), "index");
ParameterExpression item1 = Expression.Parameter(typeof(double), "item");
//Here I want to covert the "(item, index) => new { item, index }" part from LINQ expression into Reflection
}
}
Probably the most complex and useless Expression tree I've ever built by hand. Comments inline.
public class Expresion {
// We need the anonymous type that we want to use
private static readonly Type AnonymousType = new { item = 0.0, index = 0 }.GetType();
public void CreateExpression() {
//Below is the LINQ expression I want to convert
Expression<Func<Data, List<int>>> exp2 = p => p.s[new int[] { 14, 5 }].Select((item, index) => new { item, index }).Select(x => x.index).ToList();
ParameterExpression p1 = Expression.Parameter(typeof(Data), "p");
MemberExpression s1 = Expression.PropertyOrField(p1, "s");
// The indexer
PropertyInfo dictItem = s1.Type.GetProperty("Item");
// The key to the dictionary, new int[] { 14, 5 }
var key = Expression.NewArrayInit(typeof(int), Expression.Constant(14), Expression.Constant(5));
// s[new int[] { 14, 5 }]
var getItem = Expression.Property(s1, dictItem, key);
// Enumerable.Select with indexer (generic)
var enumerableSelectIndexTSourceTResult = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == "Select" && x.IsGenericMethod
let args = x.GetGenericArguments()
where args.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
pars[1].ParameterType == typeof(Func<,,>).MakeGenericType(args[0], typeof(int), args[1])
select x).Single();
// Enumerable.Select with indexer (non-generic)
var enumerableSelectIndex = enumerableSelectIndexTSourceTResult.MakeGenericMethod(typeof(double), AnonymousType);
// Inner function start
ParameterExpression item1 = Expression.Parameter(typeof(double), "item");
ParameterExpression index1 = Expression.Parameter(typeof(int), "index");
var innerExpression1 = Expression.Lambda(Expression.New(AnonymousType.GetConstructors().Single(), item1, index1), item1, index1);
// .Select((item, index) => new { item, index })
var select1 = Expression.Call(enumerableSelectIndex, getItem, innerExpression1);
// Inner function end
// Enumerable.Select without indexer (generic)
var enumerableSelectTSourceTResult = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == "Select" && x.IsGenericMethod
let args = x.GetGenericArguments()
where args.Length == 2
let pars = x.GetParameters()
where pars.Length == 2 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], args[1])
select x).Single();
// Enumerable.Select without indexer (non-generic)
var enumerableSelect = enumerableSelectTSourceTResult.MakeGenericMethod(AnonymousType, typeof(int));
// Inner function start
ParameterExpression anonymousType1 = Expression.Parameter(AnonymousType, "x");
var innerExpression2 = Expression.Lambda(Expression.Property(anonymousType1, "index"), anonymousType1);
// Inner function end
// .Select((previous select), x => x.index)
var select2 = Expression.Call(enumerableSelect, select1, innerExpression2);
// Enumerable.ToList (generic)
var enumerableToListTSource = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
where x.Name == "ToList" && x.IsGenericMethod
let args = x.GetGenericArguments()
where args.Length == 1
let pars = x.GetParameters()
where pars.Length == 1 &&
pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0])
select x).Single();
// Enumerable.ToList (non-generic)
var enumerableToList = enumerableToListTSource.MakeGenericMethod(typeof(int));
// .ToList((previous select))
var toList1 = Expression.Call(enumerableToList, select2);
var exp1 = Expression.Lambda<Func<Data, List<int>>>(toList1, p1);
var func1 = exp1.Compile();
// Test
var data = new Data();
data.S[new int[] { 14, 5 }] = new List<double> { 1.0, 2.0 };
var result = func1(data);
}
}
Note that there are some limitations: the anonymous type used must be known at compile time. Using a Tuple<> is often an alternative. In the code the Type AnonymousType line makes the compiler know the type and gets it (through the final .GetType()).
Another important part is the one about finding functions in the Enumerable class. The Select especially is quite complex to find, because there are two different Select with the same number of parameters.

Dynamic Where in Linq with Entity Framework

i wrote function
private Func<CategorizedPosts, bool> CompileExpression(IEnumerable<Category> categories)
{
Expression predicateBody;
ParameterExpression pe = Expression.Parameter(typeof(CategorizedPosts), "post");
Expression left = Expression.Property(pe, typeof(CategorizedPosts).GetProperty("CATEGORY_ID"));
Expression right = Expression.Constant(categories.ElementAt(0).ID);
Expression equal = Expression.Equal(left, right);
predicateBody = equal;
for (int i = 1, j = categories.Count() - 1; i < categories.Count(); ++i )
{
var category = categories.ElementAt(i);
//y => y.CATEGORY_ID == 1 || y.CATEGORY_ID == 2)
left = Expression.Property(pe, typeof(CategorizedPosts).GetProperty("CATEGORY_ID"));
right = Expression.Constant(category.ID);
equal = Expression.Equal(left, right);
predicateBody = Expression.OrElse(predicateBody, equal);
}
var lll = Expression.Lambda<Func<CategorizedPosts, bool>>(predicateBody, pe);
var compiled = lll.Compile();
return compiled;
}
it compiles OK, but when I try to run this query
var ctx = db.Posts.Where(x => true);
if(predicate != null)
{
ctx = ctx.Where(x => x.CategorizedPosts.Where(**predicate**).Count() > 0);
}
IList<Post> posts = ctx.OrderByDescending(x => x.CREATION_DATE).Skip((page - 1) * perPage).Take(perPage).Select(x => new Post
{
POST_ID = x.ID,
TYPE = new Type { ID = x.TYPE_ID, NAME = x.Types.NAME },
AUTHOR = new Author()
{
ID = x.AUTHOR_ID,
NAME = x.Authors.NAME,
},
CATEGORIES = x.CategorizedPosts.Select(y => new Category() { ID = y.CATEGORY_ID, NAME = y.Categories.NAME }),
CREATION_DATE = x.CREATION_DATE,
}).ToList();
EF throws exception about internal error 1025 for Entity Data Provider. How can I perform this query with dynamic where?
You could use the Contains of a collection of Ids (int) and apply it on a where, for sample:
int[] categorieIds = categories.Select(x => x.Id).ToArray();
ctx = ctx.Where(x => x.CategorizedPosts.Any(c => categorieIds .Contains(c.Id));
Some Tips
Remember the Entity Framework works with Expression<Func<T, bool>> in the Where method, not only Func<T, bool>.
You also could try to apply PredicateBuilder class which provides some extensions methods like Or, And, Not, so, you could try this:
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where(predicate).ToList();

Dynamic Build lambda with generic type

How to build dynamic lambda function like below?
item => item.AnalyzeData.Any(subitem =>
subitem.DepartmentIDs.Any(subitem2 => subitem2 == "abc"))
AnalyzeData is custom class array type, DepartmentIDs is string array type
The class of AnalyzeData has DepartmentIDs Property
So how to dynamic generator the above lambda expression via expression tree.
After some research, the problem has been resolved. now share the solution code to others who has the some issue.
[TestMethod]
[Description("Dynamic generate lambda expression")]
public void DynamicGenerateMethod1()
{
Item item1 = new Item()
{
ItemID = "test1",
AnalyzeData = new ItemAnalyzeData[]
{
new ItemAnalyzeData()
{
DepartmentIDs = new []{"Department1","Department2"}
},
}
};
Item item2 = new Item()
{
ItemID = "test2",
AnalyzeData = new ItemAnalyzeData[]
{
new ItemAnalyzeData()
{
DepartmentIDs = new []{"Department3","Department4"}
},
}
};
Expression<Func<Item, bool>> expectedExpression =
item => item.AnalyzeData.Any(subitem => subitem.DepartmentIDs.Any(subitem2 => subitem2 == "Department3"));
//Get Enumerable.Any generic method
var anyPredicate =
typeof(Enumerable).GetMethods().First(m => m.Name == "Any" && m.GetParameters().Length == 2);
#region Build inner most lambda expression : subitem2 => subitem2 == "Department3"
var subitem2Para = Expression.Parameter(typeof(string), "subitem2");
var subitem2CompareValue = Expression.Equal(subitem2Para, Expression.Constant("Department3", typeof(string)));
var subitem2CompareFun = Expression.Lambda<Func<string, bool>>(subitem2CompareValue, subitem2Para);
#endregion
#region Build center lambda expression : subitem => subitem.DepartmentIDs.Any( ... )
var subitemPara = Expression.Parameter(typeof(ItemAnalyzeData), "subitem");
var departmentIDsAnyMethodCall = anyPredicate.MakeGenericMethod(typeof(string));
var subItemDepartmentIDsCall = Expression.Call(departmentIDsAnyMethodCall, Expression.Property(subitemPara, "DepartmentIDs"), subitem2CompareFun);
var subitemCompareFun = Expression.Lambda<Func<ItemAnalyzeData, bool>>(subItemDepartmentIDsCall, subitemPara);
#endregion
#region Build outer most lambda expression : item => item.AnalyzeData.Any( ... )
var itemExpr = Expression.Parameter(typeof(Item), "item");
var analyzeAnyMethodCall = anyPredicate.MakeGenericMethod(typeof(ItemAnalyzeData));
var itemAnalyzeDataCall = Expression.Call(analyzeAnyMethodCall, Expression.Property(itemExpr, "AnalyzeData"), subitemCompareFun);
var itemCompareFun = Expression.Lambda<Func<Item, bool>>(itemAnalyzeDataCall, itemExpr);
#endregion
var method = itemCompareFun.Compile();
var actualLambdaCode = itemCompareFun.ToString();
var expectLambdaCode = expectedExpression.ToString();
Assert.AreEqual(expectLambdaCode, actualLambdaCode);
Assert.IsFalse(method.Invoke(item1));
Assert.IsTrue(method.Invoke(item2));
}

Categories