After having searched for quite a while, I am still not finding the answer I am looking for. I have found answers about adding and removing parameters from a tree, but not anything about replacing specific parameters.
My first method is working as I would like it to, I need to replace the partitionKey value with the Uri escaped value and then return the results un-escaped.
public override IList<T> GetRowEntityList(string partitionKey)
{
IList<T> rowEntities = base.GetRowEntityList(Uri.EscapeDataString(partitionKey));
return rowEntities.Select(UnEscapeRowEntity).ToList();
}
The problem I am having is overriding this method to behave the same way. I already know that type T has the properties PartitionKey and RowKey but can also have any other number of properties.
For a sample predicate:
x => x.RowKey == "foo/bar" && x.SomeValue == "test"
I would expect it to become
x => x.RowKey == Uri.EscapeDataString("foo/bar") && x.SomeValue == "test"
Is there a way to do this?
My base class uses this predicate to do a table lookup on a table containing entities of type T using a Where(predicate) call
public override IList<T> GetRowEntityList(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
//modify predicate value here
return base.GetRowEntityList(predicate);
}
You need to implement an ExpressionVisitor:
class MyVisitor : ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression node)
{
if(CheckForMatch(node.Left))
return Expression.Equal(node.Left, Rewrite(node.Right));
if(CheckForMatch(node.Right))
return Expression.Equal(Rewrite(node.Left), node.Right);
return Expression.MakeBinary(node.NodeType, Visit(node.Left), Visit(node.Right));
}
private bool CheckForMatch(Expression e)
{
MemberExpression me = e as MemberExpression;
if(me == null)
return false;
if(me.Member.Name == "RowKey" || me.Member.Name == "PartitionKey")
return true;
else
return false;
}
private Expression Rewrite(Expression e)
{
MethodInfo mi = typeof(Uri).GetMethod("EscapeDataString");
return Expression.Call(mi, e);
}
}
I think that's right. It's a bit hard to test. Please note this will only work for the limited case of (x => x.RowKey == "some string"). It won't work for (x => x.RowKey.Equals("somestring"). It also won't work for (x => x.RowKey() == "some string").
You then use the implemented visitor to re-write the predicate:
Expression<Func<T, bool>> predicate = (s => s.RowKey == "1 2");
ExpressionVisitor v = new MyVisitor();
Expression<Func<T, bool>> rewrittenPredicate = v.Visit(predicate);
//rewrittenPredicate then tests if s.RowKey == "1%202"
Related
I have to send expressions over http to my backend. This backend knows about enum Fun but doesn't have a reference to funs.
My job is to serialize exp2 in a way the backend can still deserialize it
Is there a way to force passing the enum value rather than a reference to the array element?
var funs = new[] { Fun.Low, Fun.High };
Expression<Func<Funky, bool>> exp1 = x => x.Status == Fun.Low;
Expression<Func<Funky, bool>> exp2 = x => x.Status == funs[0];
Console.WriteLine(exp1);
//Expected: x => (Convert(x.Status, Int32) == 1)
Console.WriteLine(exp2);
//Actual output: x => (Convert(x.Status, Int32) == Convert(value(Program+<>c__DisplayClass0_0).funs[0], Int32))
public enum Fun : int {
Low = 1,
Middle = 2,
High = 420
}
public class Funky {
public Fun Status {get;set;} = Fun.High;
}
Question: how can I make exp2 the same result as exp1?
_____________________________________________
Background Info:
exp1 serializes the enum value as 1 which can be correctly interpreted by the backend.
exp2 serializes funs[0] as a reference to the actual array-element, looking like this: Convert(value(Program+<>c__DisplayClass0_0).funs[0], Int32)
I also tried exp3 but this outputs the value still as a reference rather than the constant enum value.
What I've tried so far:
//another tests
var afun = new Funky();
var param = Expression.Parameter(typeof(Funky), "x");
var key = afun.GetType().GetProperty("Status");
var lhs = Expression.MakeMemberAccess(param, key);
var rhs = Expression.ArrayIndex(Expression.Constant(funs), Expression.Constant(0));
var body = Expression.Equal(lhs, rhs);
var exp3 = Expression.Lambda<Func<Funky, bool>>(body, param);
Console.WriteLine(exp3);
//x => (x.Status == value(Fun[])[0])
Real-life example:
The Backend holds a database that will be queried via EF-LINQ.
The Frontend is supposed to send the exact LINQ Query to the backend.
Lets say a User of the Frontend has a checklist, through which he can toggle which Funky objects he can query from Backend:
[x] Low
[x] Middle
[_] High
-> outputs var funs = new[] { Fun.Low, Fun.Middle };
Now the Frontend will have to put the Expression together like so:
Expression<Func<Funky, bool>> exp2 = x => x.Status == funs[0] || x.Status == funs[1];
and serialize it before it sends it to the backend.
The backend wont be able to understand funs[0] or funs[1]. But Backend knows about enum Fun and could deserialize 1 and 2 correctly.
Basically, you need to rewrite the Expression to remove all the indirection and use the literal value directly. This can be done with an ExpressionVisitor - a simplified example is shown below (it handles your scenario) - but if you want to handle more complex things like method invocations (evaluated locally), you'll need to add more override methods:
public class SimplifyingVisitor : ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression node)
{
if (node.NodeType == ExpressionType.ArrayIndex)
{
if (Visit(node.Left) is ConstantExpression left
&& left.Value is Array arr && arr.Rank == 1
&& Visit(node.Right) is ConstantExpression right)
{
var type = left.Type.GetElementType();
switch (right.Value)
{
case int i:
return Expression.Constant(arr.GetValue(i), type);
case long l:
return Expression.Constant(arr.GetValue(l), type);
}
}
}
return base.VisitBinary(node);
}
protected override Expression VisitUnary(UnaryExpression node)
{
if (node.NodeType == ExpressionType.Convert
&& Visit(node.Operand) is ConstantExpression arg)
{
try
{
return Expression.Constant(
Convert.ChangeType(arg.Value, node.Type), node.Type);
}
catch { } //best efforts
}
return base.VisitUnary(node);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.NodeType == ExpressionType.MemberAccess && Visit(node.Expression) is ConstantExpression target)
{
switch (node.Member)
{
case PropertyInfo property:
return Expression.Constant(property.GetValue(target.Value), property.PropertyType);
case FieldInfo field:
return Expression.Constant(field.GetValue(target.Value), field.FieldType);
}
}
return base.VisitMember(node);
}
}
usage:
var visitor = new SimplifyingVisitor();
exp2 = (Expression<Func<Funky, bool>>)visitor.Visit(exp2);
I am trying to make a filtering system in my web app. The problem is I don't know how many filters will be requested from my client to API. I've build it so the array of the filters comes from a single string like this: ?sizeFilters=big,small,medium
Then I use a string[] names = sizeFilters.Split(','); to get single expression like Where(x => x.listOfSizes.contains(names[index]));
I need also to make the chain of the expression using AND and OR because I am gonna use another filter for example: '?typeFilters=normal,extra,spicy'
So I need to make it that the whole expressions looks like this but it might be few times longer, it needs to work with different size of arrays:
return items Where size is big OR small OR medium AND Where type is normal OR extra OR spicy
Where(x => x.Sizes == "Small" || x => x.Sizes == "Medium" || x => x.Sizes == "Big" &&
x => x.Types == "normal" || x => x.Types == "extra" || x => x.Types == "Spicy")
The simplest option would be, as others have noted, to build your ORs using Enumerable.Contains within an expression; and to build your ANDs by calling Where multiple times.
// using these values as an example
string[] sizeTerms = /* initialize */;
string[] typeTerms = /* initialize */;
IQueryable<Item> items = /* initialize */
if (sizeTerms.Any()) {
items = items.Where(x => sizeTerms.Contains(x.Size));
}
if (typeTerms.Any()) {
items = items.Where(x => typeTerms.Contains(x.Type));
}
If you want, you could wrap this logic into an extension method that takes an expression to filter against, and an IEnumerable<string> for the filter values; and constructs and applies the Contains method:
// using System.Reflection
// using static System.Linq.Expressions.Expression
private static MethodInfo containsMethod = typeof(List<>).GetMethod("Contains");
public static IQueryable<TElement> WhereValues<TElement, TFilterTarget>(
this IQueryable<TElement> qry,
Expression<Func<TElement, TFilterTarget>> targetExpr,
IEnumerable<string> values
) {
var lst = values.ToList();
if (!lst.Any()) { return qry; }
return qry.Where(
Lambda<Expression<Func<TElement, bool>>>(
Call(
Constant(lst),
containsMethod.MakeGenericMethod(typeof(T)),
targetExpr.Body
),
targetExpr.Parameters.ToArray()
)
);
}
and can be called like this:
qry = qry
.WhereValues(x => x.Size, sizeTerms)
.WhereValues(x => x.Type, typeTerms);
One caveat: the query will be built based on the values passed into the method; if they are later changed, the query won't reflect those changes. If this is an issue:
get the appropriate overload of Enumerable.Contains, instead of List.Contains, and
use the overload of Expression.Call which produces a static method call, instead of an instance method call.
I think following should work
var query = _context.Set<[Entity]>();
if (sizeFilterPresent)
{
query = query.Where(r => sizes.Contains(r.Size));
}
if(typesFilterPresent)
{
query = query.Where(r => types.Contains(r.Type));
}
var results = query.ToList();
You can try this,
var result = data.Where(p => sizeFilters.Contains(p.Size) && typeFilters.Contains(p.Type));
You can simply call .Where multiple times to AND expressions together. Dynamically OR-ing expressions together is much more difficult. You'll need to rebuild the Expression graph to include the OrElse operator, and ensure that all expressions are based on the same ParameterExpression.
public class Replacer : ExpressionVisitor
{
private readonly Dictionary<Expression, Expression> _replacements;
public Replacer(IEnumerable<Expression> before, IEnumerable<Expression> after)
{
_replacements = new Dictionary<Expression, Expression>(before.Zip(after, (a, b) => KeyValuePair.Create(a, b)));
}
public override Expression Visit(Expression node)
{
if (node != null && _replacements.TryGetValue(node, out var replace))
return base.Visit(replace);
return base.Visit(node);
}
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
if (expr1 == null)
return expr2;
if (expr2 == null)
return expr1;
return Expression.Lambda<Func<T, bool>>(
Expression.OrElse(
expr1.Body,
new Replacer(expr2.Parameters, expr1.Parameters).Visit(expr2.Body)
),
expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
if (expr1 == null)
return expr2;
if (expr2 == null)
return expr1;
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(
expr1.Body,
new Replacer(expr2.Parameters, expr1.Parameters).Visit(expr2.Body)
),
expr1.Parameters);
}
// Usage
Expression<Func<TableObject, bool>> where = null;
if (...)
where = where.Or(x => sizeFilters.Contains(x.Size));
if (...)
where = where.Or(x => typeFilters.Contains(x.Type));
if (where!=null)
query = query.Where(where);
To make it look neat, my advice would be to create and extension method. This way you can use it as any other LINQ method. See extension methods demystified
Assuming your source is an IQuertyable<TSource>.
public static IQueryable<TSource> WhereAnd<TSource>(
this IQueryable<TSource> source,
IEnumerable<Expression<Func<TSource,bool>>> filterPredicates)
{
// TODO: handle null source, expressions;
IQueryable<TSource> filteredSource = source;
foreach (var predicate in filterPredicates)
{
filteredSource = filteredSource.Where(predicate);
}
}
Usage:
var predicates = new List<Expression<Func<TSource,bool>>>()
{
customer => customer.BirthDay.Year <= 1950,
customer => customer.CityId == GetCityId("New York"),
customer => customer.Gender == Gender.Male,
}
var oldNewYorkMaleCustomers = dbContext.Customers.WhereAnd(predicates).ToList();
Note: an empty collection of filterpredicates will filter by no predicate: you get the original data:
var emptyFilter = Queryable.Empty<Expression<Func<Customer, bool>>>();
var allCustomers = dbContext.Customers.WhereAnd(emptyFilter);
I got stuck with attempt of generic casting or building a LINQ expression that could be casted to the required generic type T.
Here is the failing example:
public override void PreprocessFilters<T>(ISpecification<T> specification)
{
if (typeof(ICompanyLimitedEntity).IsAssignableFrom(typeof(T)))
{
var companyId = 101; // not relevant, retrieving company ID here
// and now I need to call the method AddCriteria on the specification
// as it were ISpecification<ICompanyLimitedEntity>
// which it should be because at this point I know
// that T is ICompanyLimitedEntity
var cle = specification as ISpecification<ICompanyLimitedEntity>;
// ^-- but of course this conversion won't work, cle is null
// what should I do now?
// I need to call it like this:
cle.AddCriteria(e => e.CompanyId == null ||
e.CompanyId == 0 || e.CompanyId == companyId);
// BTW, AddCriteria receives Expression<Func<T, bool>> as an argument
// tried nasty casts inside of the expression - this doesn't throw NullReference
// but it doesn't translate to SQL either - gets evaluated locally in memory, which is bad
specification.AddCriteria(e => ((ICompanyLimitedEntity)e).CompanyId == null ||
((ICompanyLimitedEntity)e).CompanyId == 0 ||
((ICompanyLimitedEntity)e).CompanyId == companyId);
// this one also doesn't work
Expression<Func<ICompanyLimitedEntity, bool>> lex = e => e.CompanyId == null ||
e.CompanyId == 0 || e.CompanyId == companyId;
// it's null again -----------v
specification.AndCriteria(lex as Expression<Func<T, bool>>);
}
}
Is there any way to cast T or build a Linq expression that would treat T as ICompanyLimitedEntity and execute the query on the SQL server?
Here is how you can build the desired expression.
First you create compile time expression with interface type parameter
Expression<Func<ICompanyLimitedEntity, bool>> exprI = e => e.CompanyId == null ||
e.CompanyId == 0 || e.CompanyId == companyId;
then replace the parameter inside the body with a new parameter of type T and use the modified expression as a body for the new lambda expression:
var parameterT = Expression.Parameter(typeof(T), "e");
var bodyT = exprI.Body.ReplaceParameter(exprI.Parameters[0], parameterT);
var exprT = Expression.Lambda<Func<T, bool>>(bodyT, parameterT);
where ReplaceParameter is the typical ExpressionVisitor based helper for replacing parameter with another expression:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
=> new ParameterReplacer { Source = source, Target = target }.Visit(expression);
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
=> node == Source ? Target : node;
}
}
Note that you'll still need the member access expression fixer from Why Linq "where" expression after Select gets evaluated locally when created through a generic method?
I would like to have some somewhat complex logic kept in a single lambda expression, which can be compiled and therefore used in Linq-To-Objects, or used as an expression to run against a database in Linq-To-Entities.
it involves date calculations, and I have hitherto been using something like (hugely simplified)
public static Expression<Func<IParticipant, DataRequiredOption>> GetDataRequiredExpression()
{
DateTime twentyEightPrior = DateTime.Now.AddDays(-28);
return p=> (p.DateTimeBirth > twentyEightPrior)
?DataRequiredOption.Lots
:DataRequiredOption.NotMuchYet
}
And then having a method on a class
public DataRequiredOption RecalculateDataRequired()
{
return GetDataRequiredExpression().Compile()(this);
}
There is some overhead in compiling the expression tree. Of course I cannot simply use
public static Expression<Func<IParticipant, DataRequiredOption>> GetDataRequiredExpression(DateTime? dt28Prior=null)
{
return p=> DbFunctions.DiffDays(p.DateTimeBirth, DateTime.Now) > 28
?DataRequiredOption.Lots
:DataRequiredOption.NotMuchYet
}
Because this will only run at the database (it will throw an error at execution of the Compile() method).
I am not very familiar with modifying expressions (or the ExpressionVisitor class). Is it possible, and if so how would I find the DbFunctions.DiffDays function within the expression tree and replace it with a different delegate? Thanks for your expertise.
Edit
A brilliant response from svick was used - a slight modification beecause difdays and date subtraction have their arguments switched to produce a positive number in both cases:
static ParticipantBaseModel()
{
DataRequiredExpression = p =>
((p.OutcomeAt28Days >= OutcomeAt28DaysOption.DischargedBefore28Days && !p.DischargeDateTime.HasValue)
|| (DeathOrLastContactRequiredIf.Contains(p.OutcomeAt28Days) && (p.DeathOrLastContactDateTime == null || (KnownDeadOutcomes.Contains(p.OutcomeAt28Days) && p.CauseOfDeath == CauseOfDeathOption.Missing))))
? DataRequiredOption.DetailsMissing
: (p.TrialArm != RandomisationArm.Control && !p.VaccinesAdministered.Any(v => DataContextInitialiser.BcgVaccineIds.Contains(v.VaccineId)))
? DataRequiredOption.BcgDataRequired
: (p.OutcomeAt28Days == OutcomeAt28DaysOption.Missing)
? DbFunctions.DiffDays(p.DateTimeBirth, DateTime.Now) < 28
? DataRequiredOption.AwaitingOutcomeOr28
: DataRequiredOption.OutcomeRequired
: DataRequiredOption.Complete;
var visitor = new ReplaceMethodCallVisitor(
typeof(DbFunctions).GetMethod("DiffDays", BindingFlags.Static | BindingFlags.Public, null, new Type[]{ typeof(DateTime?), typeof(DateTime?)},null),
args =>
Expression.Property(Expression.Subtract(args[1], args[0]), "Days"));
DataRequiredFunc = ((Expression<Func<IParticipant, DataRequiredOption>>)visitor.Visit(DataRequiredExpression)).Compile();
}
Replacing a call to a static method to something else using ExpressionVisitor is relatively simple: override VisitMethodCall(), in it check if it's the method that you're looking for and if it, replace it:
class ReplaceMethodCallVisitor : ExpressionVisitor
{
readonly MethodInfo methodToReplace;
readonly Func<IReadOnlyList<Expression>, Expression> replacementFunction;
public ReplaceMethodCallVisitor(
MethodInfo methodToReplace,
Func<IReadOnlyList<Expression>, Expression> replacementFunction)
{
this.methodToReplace = methodToReplace;
this.replacementFunction = replacementFunction;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method == methodToReplace)
return replacementFunction(node.Arguments);
return base.VisitMethodCall(node);
}
}
The problem is that this won't work well for you, because DbFunctions.DiffDays() works with nullable values. This means both its parameters and its result are nullable and the replacementFunction would have to deal with all that:
var visitor = new ReplaceMethodCallVisitor(
diffDaysMethod,
args => Expression.Convert(
Expression.Property(
Expression.Property(Expression.Subtract(args[0], args[1]), "Value"),
"Days"),
typeof(int?)));
var replacedExpression = visitor.Visit(GetDataRequiredExpression());
To make it work better, you could improve the visitor to take care of the nullability for you by stripping it from the method arguments and then readding it to the result, if necessary:
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method == methodToReplace)
{
var replacement = replacementFunction(
node.Arguments.Select(StripNullable).ToList());
if (replacement.Type != node.Type)
return Expression.Convert(replacement, node.Type);
}
return base.VisitMethodCall(node);
}
private static Expression StripNullable(Expression e)
{
var unaryExpression = e as UnaryExpression;
if (unaryExpression != null && e.NodeType == ExpressionType.Convert
&& unaryExpression.Operand.Type == Nullable.GetUnderlyingType(e.Type))
{
return unaryExpression.Operand;
}
return e;
}
Using this, the replacement function becomes much more reasonable:
var visitor = new ReplaceMethodCallVisitor(
diffDaysMethod,
args => Expression.Property(Expression.Subtract(args[0], args[1]), "Days"));
Hi and thanks for taking the time to answer my question.
After a year and a half of working with Java, I've decided to switch back to .NET. I must say that I feel at home in VS2012.
While working with Java I came across an implementation of hibernate that enabled for creating dynamic queries easily.
Imagine I had a form with 5 fields of which only one, any one, must be populated in order for me to filter the results by.
is there a way I can do the following in C#:
if(txtMunicipality.text.length > 0){
(x => x.municipality == txtMunicipality.text)
}
if(chkboxIsFinished){
(x => x.isfinished == true)
}
etc..
So I ccheck for every field and if the value has been populated then add that criteria to the query.. and after i'm done with the checks i execute the query. Is there a way to do this in C#?
The simplest way to do this is to compose two queries, i.e.
IQueryable<Foo> query = ... // or possibly IEnumerable<Foo>
if(!string.IsNullOrEmpty(txtMunicipality.text)) {
query = query.Where(x => x.municipality == txtMunicipality.text);
}
if(chkboxIsFinished) {
query = query.Where(x.isfinished);
}
You can also directly compose expression-trees and delegates; if you need that, please indicate which you have: an expression-tree vs a delegate.
Edit: here's how you would do that composing expressions rather than queries:
static class Program
{
static void Main()
{
Expression<Func<int, bool>> exp1 = x => x > 4;
Expression<Func<int, bool>> exp2 = x => x < 10;
Expression<Func<int, bool>> exp3 = x => x == 36;
var combined = (exp1.AndAlso(exp2)).OrElse(exp3);
// ^^^ equiv to x => (x > 4 && x < 10) || x == 36
}
static Expression<Func<T, bool>> OrElse<T>(this Expression<Func<T, bool>> x, Expression<Func<T, bool>> y)
{ // trivial cases
if (x == null) return y;
if (y == null) return x;
// rewrite using the parameter from x throughout
return Expression.Lambda<Func<T, bool>>(
Expression.OrElse(
x.Body,
SwapVisitor.Replace(y.Body, y.Parameters[0], x.Parameters[0])
), x.Parameters);
}
static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> x, Expression<Func<T, bool>> y)
{ // trivial cases
if (x == null) return y;
if (y == null) return x;
// rewrite using the parameter from x throughout
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(
x.Body,
SwapVisitor.Replace(y.Body, y.Parameters[0], x.Parameters[0])
), x.Parameters);
}
class SwapVisitor : ExpressionVisitor
{
public static Expression Replace(Expression body, Expression from, Expression to)
{
return new SwapVisitor(from, to).Visit(body);
}
private readonly Expression from, to;
private SwapVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
}
Yes, it is possible. The simplest way is with delegates, especially the anonymous ones.
For example:
Func<YourEntity, bool> filter = (_ => true); // Default value.
if (txtMunicipality.text.length > 0)
{
filter = (x => x.municipality == txtMunicipality.text);
}
else if (chkboxIsFinished)
{
filter = (x => x.isfinished == true);
}
Then you can use the filter delegate in a query, for example in a Where statement (which I suppose was your intent - if not, the example is still relevant, just not directly applicable)
/ LINQ syntax.
var entities = from e in context
where filter(e)
select e;
// Method syntax.
var entities = context.Where(x => filter(x));
// Or simply:
var entities = context.Where(filter);
In this article you can find some useful extension methods that allows you to combine predicates (it should work for NHibernate as well):
LINQ to Entities: Combining Predicates
You can then build a lambda expression like this:
Expression<Func<MyObject, bool>> predicate = x => true;
if(txtMunicipality.text.length > 0){
predicate = predicate.And(x => x.municipality == txtMunicipality.text);
}
if(chkboxIsFinished){
predicate = predicate.And(x => x.isfinished == true);
}