I have a repository class that allows for queries using a lambda expression (simplified partial code):
public class SomeRepository<T>: IRepository<T>
{
public IList<T> Find(Expression<Func<T, bool>> filter)
{
return SomeQueryProvider.Where(filter).ToList();
}
}
However, for a specific provider I have the need to wrap the original object in another class:
public class Wrapped<T>
{
public T OriginalObject { get; set; }
}
So in this case, I also need to wrap the incoming predicate expression in another expression:
public class AnotherRepository<T>: IRepository<T>
{
public IList<T> Find(Expression<Func<T, bool>> filter)
{
Expression<Func<Wrapped<T>, bool>> wrappedFilter = ...
return AnotherQueryProvider.Where(wrappedFilter).ToList();
}
}
For example x => x.ParentId == 123 should become x => x.OriginalObject.ParentId == 123.
I can't find examples for this scenario, and I'm having difficulty solving this myself. How can I prepend the predicate expression with the OriginalObject property?
Answering the concrete question.
Given expression
Expression<Func<Wrapped<T>, T>> e1 = w => w.OriginalObject;
converting the expression
Expression<Func<T, bool>> e2 = o => o.ParentId == 123;
to
Expression<Func<Wrapped<T>, T>> e3 = w => w.OriginalObject.ParentId == 123;
is a matter of replacing the o parameter occurrences inside the e2 body with w.OriginalObject (the body of the e1). Something like string replace, but for expressions :)
First you need a method that replaces expression parameter with something else. Here is the one that I use:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
Now the method in question could be like this:
partial class ExpressionUtils
{
public static Expression<Func<Wrapped<T>, TResult>> ToWrapped<T, TResult>(this Expression<Func<T, TResult>> source)
{
Expression<Func<Wrapped<T>, T>> unwrap = w => w.OriginalObject;
var parameter = unwrap.Parameters[0];
var body = source.Body.ReplaceParameter(source.Parameters[0], unwrap.Body);
return Expression.Lambda<Func<Wrapped<T>, TResult>>(body, parameter);
}
}
and the usage
var wrappedFilter = filter.ToWrapped();
Related
I have one expression that's defined like this:
Expression<Func<T1, T2, bool>> firstExpression;
and another one like this one:
Expression<Func<T1, bool>> secondExpression;
T2 is a value I know, T1 isn't. What I would like is to make the first expression into the second one, given the value of the parameter T2. If it would be regular Linq, it would be this:
var t2 = "Something I know";
secondExpression = t1 => fistExpression(t1, t2);
How would I do this using System.Linq.Expressions?
You can accomplish this with expression visitor:
public static class EmitUtils
{
private class ParameterReplacerVisitor : ExpressionVisitor
{
private ParameterExpression _source;
private Expression _target;
public ParameterReplacerVisitor(ParameterExpression source, Expression target)
{
_source = source;
_target = target;
}
public override Expression Visit(Expression node) =>
node == _source
? _target
: base.Visit(node);
}
public static Expression ReplaceParameter(Expression body, ParameterExpression srcParameter, Expression dstParameter) =>
new ParameterReplacerVisitor(srcParameter, dstParameter).Visit(body);
public static Expression<Func<T1, T3>> BuildClosure<T1, T2, T3>(Expression<Func<T1, T2, T3>> src, T2 closureValue)
{
var constExpression = Expression.Constant(closureValue, typeof(T2));
var body = ReplaceParameter(src.Body, src.Parameters[1], constExpression);
return Expression.Lambda<Func<T1, T3>>(body, src.Parameters[0]);
}
}
Here is sample usage:
[Test]
public void ClosureTest()
{
Expression<Func<int, string, bool>> CheckStringLength = (len, str) => str.Length < len;
var constString = "some string";
var result = EmitUtils.BuildClosure(CheckStringLength, constString);
Assert.That(result.Compile().Invoke(100), Is.True);
}
You can do it by swapping parameter expression with constant expression. Should looks like this
implement custom swap ExpressionVisitor
public class SwapVisitor : ExpressionVisitor
{
public Expression From { get; set; }
public Expression To { get; set; }
public override Expression Visit(Expression node)
{
return node == From ? To : base.Visit(node);
}
}
replace parameter T2 with constant
var swapper = new SwapVisitor
{
From = fistExpression.Parameters[1],
To = Expression.Constant(val)
};
var result = Expression.Lambda<Func<T, bool>>(
swapper.Visit(fistExpression.Body),
fistExpression.Parameters[0]);
Consider the following sample(firstLambda ans secondExpression needed just to see how compiler build the expression)
using System;
using System.Linq.Expressions;
static void Main(string[] args)
{
Expression<Func<string, int, bool>> firstExpression = (a, b) => a == b.ToString();
Func<string, int, bool> firstLambda = (a, b) => a == b.ToString();
Expression<Func<string, bool>> secondExpression = s => firstLambda(s, 45); // this expression I need just to see how it is compiled
var inputParameter = Expression.Parameter(typeof(string), "s");
var invocation = Expression.Invoke(firstExpression, inputParameter, Expression.Constant(47));
var ourBuildExpression = Expression.Lambda<Func<string, bool> > (invocation, new ParameterExpression[] { inputParameter }).Compile();
Console.WriteLine(ourBuildExpression("45"));
Console.WriteLine(ourBuildExpression("47"));
Console.ReadKey();
}
I checked in the debugger watch window the result of Expression<Func<string, bool>> secondExpression = s => firstLambda(s, 45); it was Invoke expression, so I constructed the same manually.
And you can see the test calls return and print False and True as expected.
I am trying to perform a query against a list to give immediate results using an expression that is set elsewhere in the code, while a second thread goes off and uses it to get a full set of results from a database in a Linq query.
I know that the expression itself is OK as when I send it over the wire to the server side and apply it against an IQueryable then it will work. However, when applied on the client side the following error is produced:
System.InvalidOperationException: 'variable 'item' of type 'MissionControlSuite.Shared.Interface.Model.IBox' referenced from scope '', but it is not defined'
This code that performs the filtering:
public abstract class GridWithPaging<T> where T : class
{
List<T> _items
public Expression<Func<T, bool>> Filter { get; set; }
public ObservableCollection<IGridFilter<T>> Filters { get; set; }
// more code skipped for breveity
public void FiltersChanged(object sender, EventArgs e)
{
Expression<Func<T, bool>> combined = item => true;
foreach (var filter in Filters)
combined = Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(filter.Expression.Body, combined.Body),
combined.Parameters.Single());
Filter = combined;
NotifyPropertyChanged("Filter");
}
public void AddFilter(IGridFilter<T> filter)
{
Filters.Add(filter);
NotifyPropertyChanged("Filters");
}
private void DoFiltering(int start)
{
var newView = _items.Skip(start);
if(Filter != null)
newView = newView.AsQueryable().Where(Filter);
//some more code that acts on the filtered list
}
}
The expression is set elsewhere like this:
Expression<Func<IBox, bool>> expression = item => item.BoxId.Contains(v);
var filter = new GridFilter<IBox>()
{
Name = string.Format("{0} contains {1}", Property, Value),
Expression = expression
};
Grid.AddFilter(filter);
This answer will help you: Combining two expressions (Expression<Func<T, bool>>)
To summarize: The problem is that the parameters, although with the same name, are not the same instance of the ParameterExpression - which is included in the comment to that answer.
The solution is to use a Visitor (code copied over from linked post):
public static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof (T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
Also note: the ORM provider (or another IQueryable you use) probably works because it translates that to text directly, whereas executing (Invokeing) this LambdaExpression causes the error as it's more strict, in a way. Some IQueryables can also accept simple string parameters and parse that on their own, which should indicate that they have a wider range of acceptable inputs.
I'm trying to do a LinQ request with a group by where a parameter is parametrizable by an Expression ( (Expression<Func<CompanyModel,TKey>> myGroupingProperty) and the other one is hard coded. But even if my code compile I get an error that linq does not support lambdas. Would you have any idea how to do this request?
Here is the code sample:
public List<timelineResult> getTimelinebyCompany<TKey>(Expression<Func<CompanyModel,TKey>> myGroupingProperty, Filter item)
{
int cntToBeSureThatTheQueryExecuteAtLeastOneTime = 0;
List<timelineResult> toto = new List<timelineResult>();
using (var db = new fintechDbContext())
{
while (cntToBeSureThatTheQueryExecuteAtLeastOneTime == 0)
{
toto = (from p in db.companyDBSET
select p).GroupBy(p=> new {p.Founded_Year, myGroupingProperty})
.Select(o => new timelineResult{ year = o.Key.Founded_Year, cluster = o.myGroupingProperty.ToString(), count = o.Count() })
.OrderBy(o => o.year).ToList();
cntToBeSureThatTheQueryExecuteAtLeastOneTime++;
}
}
return toto;
}
What you are seeking for is doable, but not the way you tried because the passed lambda expression cannot be used directly inside another lambda expression.
You should start first by creating a generic class to hold the grouping key. It's similar to system provided Tuple, but has parameterless constructor and simple property get/setters to conform to the EF projection rules:
public class GroupKey<K1, K2>
{
public K1 Key1 { get; set; }
public K2 Key2 { get; set; }
}
Then you need to build dynamically lambda expression like this
Expression<Func<T, K1, K2>> keySelector = x =>
new GroupKey<K1, K2> { Key1 = x.Prop1, Key2 = x.Prop2 };
In order to do that, you'll need some Expression helpers:
public static class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
and you can encapsulate the grouping part in a custom extension method:
public static class QueryableExtensions
{
public static IQueryable<IGrouping<GroupKey<K1, K2>, T>> GroupByPair<T, K1, K2>(this IQueryable<T> source, Expression<Func<T, K1>> keySelector1, Expression<Func<T, K2>> keySelector2)
{
var parameter = keySelector1.Parameters[0];
var key1 = keySelector1.Body;
var key2 = keySelector2.Body.ReplaceParameter(keySelector2.Parameters[0], parameter);
var keyType = typeof(GroupKey<K1, K2>);
var keySelector = Expression.Lambda<Func<T, GroupKey<K1, K2>>>(
Expression.MemberInit(
Expression.New(keyType),
Expression.Bind(keyType.GetProperty("Key1"), key1),
Expression.Bind(keyType.GetProperty("Key2"), key2)),
parameter);
return source.GroupBy(keySelector);
}
}
Finally, the essential part of your method becomes like this:
toto = db.companyDBSET
.GroupByPair(p => p.Founded_Year, myGroupingProperty)
.Select(g => new timelineResult
{
year = g.Key.Key1,
cluster = g.Key.Key2.ToString(),
count = g.Count()
})
.OrderBy(o => o.year)
.ToList();
I would like to have generic method for filtering Entity Framework IQueryable<TItem> by IEnumerable<TEnum>. Its signature should probably look like this:
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TItem, IEnumerable<TEnum>, bool>> predicate)
{
return items.Where(??????);
}
and I would want to be able to call it for example like this:
IQueryable<Request> requests = service.GetAllRequests();
IEnumerable<RequestState> states = new RequestState[] {RequestState.Active, RequestState.Closed};
Expression<Func<Request, IEnumerable<RequestState>, bool>> predicate = (r, s) => s.Contains(r.State);
requests = ApplyFilter(requests, states, predicate);
But what should be inside method's body? How can I convert Expression<Func<TItem, IEnumerable<TEnum>, bool>> to Expression<Func<TItem, bool>> for use as parameter to "Where" method? Will it work with Entity Framework?
IMO, the predicate should be inside your ApplyFilter method (respect of concerns).
One possible way to code this would be :
public static IQueryable<TItem> ApplyFilter<TItem, TEnum>(IQueryable<TItem> items,
IEnumerable<TEnum> enumValues, Expression<Func<TItem,TEnum>> enumField)
{
return items.Where(i => enumValues.Contains(enumField(i)));
}
with a call like this :
requests = ApplyFilter(requests, states, r => r.State);
When I was implementing Sharped's answer I found that it is actually possible to do exactly what I want. The point is to use custom class that inherits from ExpressionVisitor. This class will replace every occurrence of IEnumerable<TEnum> parameter within expression tree with its actual value. My final code:
public static IQueryable<TFilteredItem> ApplyFilter<TFilteredItem, TEnum>(IQueryable<TFilteredItem> items, IEnumerable<TEnum> enumValues, Expression<Func<TFilteredItem, IEnumerable<TEnum>, bool>> predicate)
{
ParameterExpression itemParam = predicate.Parameters[0];
ParameterExpression enumsParam = predicate.Parameters[1];
var em = new ExpressionModifier<IEnumerable<TEnum>>(enumsParam.Name, enumValues);
Expression predicateBody = em.Modify(predicate.Body);
return items.Where(Expression.Lambda<Func<TFilteredItem, bool>>(predicateBody, new[] { itemParam }));
}
public class ExpressionModifier<T> : ExpressionVisitor
{
private readonly string parameterName;
private readonly T newValue;
public Expression Modify(Expression expression)
{
return Visit(expression);
}
public ExpressionModifier(string parameterName, T newValue)
{
this.parameterName = parameterName;
this.newValue = newValue;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node.Name == this.parameterName ? Expression.Constant(this.newValue, node.Type) : base.VisitParameter(node);
}
}
And I am using it like:
var requests = this.ServiceManager.Messaging.GetRequests();
var states = new[] {RequestState.Active};
requests = ApplyFilter(requests, states, (i, e) => e.Contains(i.State));
I have an Expression in the following form:
Expression<Func<T, bool>> predicate = t => t.Value == "SomeValue";
Is it possible to create a 'partially applied' version of this expression:
Expression<Func<bool>> predicate = () => t.Value == "SomeValue";
NB This expression is never actually compiled or invoked, it is merely inspected to generate some SQL.
This could be easily achieved by writing a custom ExpressionVisitor and replacing the parameter with a constant expression that you have captured in a closure:
public class Foo
{
public string Value { get; set; }
}
public class ReplaceVisitor<T> : ExpressionVisitor
{
private readonly T _instance;
public ReplaceVisitor(T instance)
{
_instance = instance;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return Expression.Constant(_instance);
}
}
class Program
{
static void Main()
{
Expression<Func<Foo, bool>> predicate = t => t.Value == "SomeValue";
var foo = new Foo { Value = "SomeValue" };
Expression<Func<bool>> result = Convert(predicate, foo);
Console.WriteLine(result.Compile()());
}
static Expression<Func<bool>> Convert<T>(Expression<Func<T, bool>> expression, T instance)
{
return Expression.Lambda<Func<bool>>(
new ReplaceVisitor<T>(instance).Visit(expression.Body)
);
}
}
I think this should work:
Expression predicate2 = Expression.Invoke(predicate, Expression.Constant(new T() { Value = "SomeValue"}));
Expression<Func<bool>> predicate3 = Expression.Lambda<Func<bool>>(predicate2);
Don't know if this is easily parseable to generate SQL however (it works when compiled - I tried).