Closed sunnycase closed 6 years ago
This might actually be a very good idea. Will research the possibilities. Interested in doing a PR?
There is an trial implementation in our project, but it's under development and I can't promise it will fit your developing guidlines.
You can keep pushing commits to a PR, so we can always adjust :)
I am using this project with Arch/UnitOfWork for built Expressions. The extention code in progress. Now implements boolean and part of string
using Catel;
using Orc.FilterBuilder.Models;
using System;
using System.Linq;
using System.Linq.Expressions;
namespace Orc.FilterBuilder.Conditions
{
public static class Conditions_Extention
{
public static Expression<Func<T, bool>> MakeFunction<T>(this ConditionTreeItem conditionTreeItem)
{
var type = typeof(T);
var pe = Expression.Parameter(type, "item");
Expression expression = conditionTreeItem.MakeExpression(pe);
if (expression == null)
{
return null;
}
var lambda = Expression.Lambda<Func<T, bool>>(expression, pe);
return lambda;
}
private static Expression MakeExpression(this ConditionTreeItem conditionTreeItem, ParameterExpression parametr)
{
if (conditionTreeItem.GetType() == typeof(ConditionGroup))
{
return ((ConditionGroup)conditionTreeItem).MakeExpression(parametr);
}
else if (conditionTreeItem.GetType() == typeof(PropertyExpression))
{
return ((PropertyExpression)conditionTreeItem).MakeExpression(parametr);
}
else
{
return null;
}
}
private static Expression MakeExpression(this ConditionGroup conditionGroup, ParameterExpression parametr)
{
if (!conditionGroup.Items.Any())
{
return null;
}
Expression final = null;
Expression left = null;
Expression rigth = null;
foreach (var item in conditionGroup.Items)
{
var curExp = item?.MakeExpression(parametr);
if (curExp == null) continue;
if (left == null)
{
left = curExp;
}
else
{
rigth = curExp;
if (conditionGroup.Type == ConditionGroupType.And)
{
final = Expression.AndAlso(left, rigth);
}
else
{
final = Expression.OrElse(left, rigth);
}
left = final;
rigth = null;
}
}
return final != null ? final : left;
}
private static Expression MakeExpression(this PropertyExpression propertyExpression, ParameterExpression pe)
{
return propertyExpression.DataTypeExpression.MakeExpression(pe, propertyExpression.Property);
}
private static Expression MakeExpression(this DataTypeExpression dataTypeExpression, ParameterExpression pe, IPropertyMetadata propertyMetadata)
{
if (dataTypeExpression.GetType() == typeof(BooleanExpression))
{
return ((BooleanExpression)dataTypeExpression).MakeExpression(pe, propertyMetadata.Name);
}
else if (dataTypeExpression.GetType() == typeof(StringExpression))
{
return ((StringExpression)dataTypeExpression).MakeExpression(pe, propertyMetadata.Name);
}
else
{
return null;
}
}
private static Expression MakeExpression(this BooleanExpression expression, ParameterExpression pe, string propertyName)
{
var Value = expression.Value;
var SelectedCondition = expression.SelectedCondition;
switch (SelectedCondition)
{
case Condition.EqualTo:
Expression left = Expression.Property(pe, propertyName);
Expression rigth = Expression.Constant(true);
Expression e = Expression.Equal(left, rigth);
return e;
default:
throw new NotSupportedException(string.Format(LanguageHelper.GetString("FilterBuilder_Exception_Message_ConditionIsNotSupported_Pattern"), SelectedCondition));
}
}
private static Expression MakeExpression(this StringExpression expression, ParameterExpression pe, string propertyName)
{
var Value = expression.Value;
var SelectedCondition = expression.SelectedCondition;
Expression left;
Expression rigth;
Expression e;
switch (SelectedCondition)
{
case Condition.Contains:
left = Expression.Property(pe, propertyName);
rigth = Expression.Constant(Value);
e = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), rigth);
return e;
case Condition.DoesNotContain:
left = Expression.Property(pe, propertyName);
rigth = Expression.Constant(Value);
e = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), rigth);
Expression final = Expression.Not(e);
return final;
case Condition.EndsWith:
left = Expression.Property(pe, propertyName);
rigth = Expression.Constant(Value);
e = Expression.Call(left, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), rigth);
return e;
case Condition.DoesNotEndWith:
// pe = Expression.Parameter(typeof(string), propertyMetadata.Name);
rigth = Expression.Constant(Value);
left = Expression.Call(pe, typeof(string).GetMethod("EndsWith"), rigth);
e = Expression.IsFalse(left);
return e;
case Condition.EqualTo:
// pe = Expression.Parameter(typeof(string), propertyMetadata.Name);
left = Expression.Property(pe, typeof(string).GetProperty(propertyName));
rigth = Expression.Constant(Value);
e = Expression.Equal(left, rigth);
return e;
case Condition.GreaterThan:
return null;
case Condition.GreaterThanOrEqualTo:
return null;
case Condition.IsEmpty:
return null;
case Condition.IsNull:
return null;
case Condition.LessThan:
return null;
case Condition.LessThanOrEqualTo:
return null;
case Condition.NotEqualTo:
return null;
case Condition.NotIsEmpty:
return null;
case Condition.NotIsNull:
return null;
case Condition.StartsWith:
return null;
case Condition.DoesNotStartWith:
return null;
case Condition.DoesNotMatch:
return null;
default:
throw new NotSupportedException(string.Format(LanguageHelper.GetString("FilterBuilder_Exception_Message_ConditionIsNotSupported_Pattern"), SelectedCondition));
}
}
}
}
`
The Code of Tests
`using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
using Orc.FilterBuilder.Models;
using Orc.FilterBuilder.Conditions;
using System.Linq.Expressions;
namespace Orc.FilterBuilder.Tests
{
public class TestLinqExpression
{
[Fact]
public void Bool_ExpressionTest()
{
var people = People.AsQueryable();
var type = typeof(Human);
var conditionGroup = new ConditionGroup()
{
Type = ConditionGroupType.And
};
var propertyExpression = new PropertyExpression()
{
Property = new PropertyMetadata(type, type.GetProperty("HasChailds")),
DataTypeExpression = new BooleanExpression()
{
SelectedCondition = Condition.EqualTo,
Value = true
}
};
conditionGroup.Items.Add(propertyExpression);
var expression = conditionGroup.MakeFunction<Human>();
var result = people.Where(expression).ToList();
Assert.Equal(result.Count, 1);
Assert.Equal(result[0].Name, "Sergio");
}
[Fact]
public void String_ExpressionTest()
{
var items = CalcHumansFor("Name", new StringExpression()
{
SelectedCondition = Condition.Contains,
Value = "nn"
});
Assert.Equal(items.Count, 1);
Assert.Equal(items[0].Name, "Ann");
items = CalcHumansFor("Name", new StringExpression()
{
SelectedCondition = Condition.DoesNotContain,
Value = "nn"
});
Assert.Equal(items.Count, 1);
Assert.Equal(items[0].Name, "Sergio");
items = CalcHumansFor("Name", new StringExpression()
{
SelectedCondition = Condition.EndsWith,
Value = "io"
});
Assert.Equal(items.Count, 1);
Assert.Equal(items[0].Name, "Sergio");
items = CalcHumansFor("Name", new StringExpression()
{
SelectedCondition = Condition.EndsWith,
Value = "n"
});
Assert.Equal(items.Count, 1);
Assert.Equal(items[0].Name, "Ann");
}
List<Human> CalcHumansFor(string propertyName, DataTypeExpression dataTypeExpression)
{
var conditionGroup = new ConditionGroup()
{
Type = ConditionGroupType.And
};
var type = typeof(Human);
var propertyExpression = new PropertyExpression()
{
Property = new PropertyMetadata(type, type.GetProperty(propertyName)),
DataTypeExpression = dataTypeExpression
};
conditionGroup.Items.Add(propertyExpression);
var expression = conditionGroup.MakeFunction<Human>();
var result = People.AsQueryable().Where(expression).ToList();
return result;
}
[Fact]
public void LinqBuild_Test()
{
var people = People.AsQueryable();
var type = typeof(Human);
var item = Expression.Parameter(type, "human");
var prop = Expression.Property(item, "HasChailds");
var soap = Expression.Constant(true);
var equal = Expression.Equal(prop, soap);
var lambda = Expression.Lambda<Func<Human, bool>>(equal, item);
var result = people.Where(lambda).ToList();
Assert.Equal(result.Count, 1);
Assert.Equal(result[0].Name, "Sergio");
}
public List<Human> People => new List<Human>()
{
new Human("Ann",5,false),
new Human("Sergio", 35, true )
};
public class Human
{
public Human(string name, int age, bool hasChailds)
{
Name = name; Age = age; HasChailds = hasChailds;
}
public string Name { get; set; }
public int Age { get; set; }
public bool HasChailds { get; set; }
}
}
}
P.S. In Progress. Many Errors in MakeExpression(this
StringExpression expression`
Ok. Now I have code/tests to build Expressions for bool, bool? and string May I add this code to 'Pull requests' or I must write all other functions?
You can create a PR Work In Progress. Then you can just keep pushing to the same PR branch and it will auto-update. Just make sure not to make any breaking changes.
PR Linq extensions #39 ready to merge. Theare no RegularExpressions only. I don`t now how implement this to SQL. There no changes in parent projects - static extentions only.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
It will be efficient to use a compiled lambda expression to filter the collection. And lambda expressions can be directly passed to entity framework to query the database. So it's a cool feature I want the library will have in the future.