I have the two expression trees : one to get a navigation property of a class and another to filter on the values from this navigation property :
I am trying to combine them .
class Program
{
public interface IDeletable
{
bool IsDeleted { get; set; }
}
public class User
{
public int Id { get; set; }
public IEnumerable<BlogPost> BlogPosts;
}
public class BlogPost : IDeletable
{
public string Text { get; set; }
public bool IsDeleted { get; set; }
}
static void Main(string[] args)
{
var user = new User()
{
Id = 1,
BlogPosts = new List<BlogPost> {
new BlogPost {IsDeleted=false,Text="hello" },
new BlogPost {IsDeleted=true,Text="this is deleted" }
}
};
Expression<Func<User, IEnumerable<BlogPost>>> notDeletedExpression = Combine<User, BlogPost>(x => x.BlogPosts, x => !x.IsDeleted);
Console.ReadLine();
}
public static Expression<Func<T, IEnumerable<TChild>>> Combine<T, TChild>
(
Expression<Func<T, IEnumerable<TChild>>> navigationProperty,
Expression<Func<TChild, bool>> filter
)
where T : class
where TChild : class, IDeletable
{
//TODO
// shourld return x=>x.Posts.Where(p=>IsDeleted==false) ;
return null;
}
}
In the sample below the two Expressions from your sample are combined using Enumerable.Where method:
public static Expression<Func<T, IEnumerable<TChild>>> Combine<T, TChild>
(
Expression<Func<T, IEnumerable<TChild>>> navigationProperty,
Expression<Func<TChild, bool>> filter
)
where T : class
where TChild : class, IDeletable
{
// Consider caching the MethodInfo object:
var whereMethodInfo = GetEnumerableWhereMethodInfo<TChild>();
// Produce an Expression tree like:
// Enumerable.Where(<navigationProperty>, <filter>)
var filterExpr = Expression
.Call(
whereMethodInfo,
navigationProperty.Body,
filter
);
// Create a Lambda Expression with the parameters
// used for `navigationProperty` expression
return Expression
.Lambda<Func<T, IEnumerable<TChild>>>(
filterExpr,
navigationProperty.Parameters
);
}
private static MethodInfo GetEnumerableWhereMethodInfo<TSource>()
{
// Get a MethodInfo definition for `Enumerable.Where<>`:
var methodInfoDefinition = typeof(Enumerable)
.GetMethods()
.Where(x => x.Name == nameof(Enumerable.Where))
.First(x =>
{
var parameters = x.GetParameters();
return
parameters.Length == 2 &&
parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>) &&
parameters[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>);
});
// Get a MethodInfo object for `Enumerable.Where<TSource>`:
var methodInfo = methodInfoDefinition.MakeGenericMethod(typeof(TSource));
return methodInfo;
}
Related
I want a entity that filters the list entity within the entity.
I have entities with common structure like below
public class FoodMenuT
{
public int Id {get;set;}
public int LanguageId {get;set;}
public string Name {get;set;}
}
public class FoodMenu
{
public int Id {get;set;}
public IEnumerable<FoodMenuT> FoodMenuTList {get;set;}
}
public static Expression<Func<FoodMenu, IEnumerable<FoodMenuT>>> LanguageFilter()
{
return e => e.FoodMenuT.Where(x => x.LanguageId == 1);
}
What I want to do is to do this in a generic way.
public static Expression<Func<T, IEnumerable<TT>>> LanguageFilter<T, TT>()
{
}
Thank you in advance for your helps.
public static Expression<Func<T, IEnumerable<TT>>> LanguageFilter<T, TT>()
{
var headerInfo = ServiceTool.ServiceProvider.GetService<IHeaderInfo>();
var entity = typeof(T);
var ttEntity = typeof(TT);
var propertyInfo = entity.GetProperty(ttEntity.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
var arg = Expression.Parameter(entity, "e");
var property = Expression.Property(arg, propertyInfo!.Name);
//e=>e.FoodMenuT
var selector = Expression.Lambda(property, new ParameterExpression[] { arg });
var propertyInfo2 = ttEntity.GetProperty("LanguageId", BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
var arg2 = Expression.Parameter(ttEntity, "x");
var property2 = Expression.Property(arg2, propertyInfo2!.Name);
var languageId = Expression.Constant(headerInfo!.LanguageId);
var equalLanguage = Expression.Equal(property2, languageId);
//x=>x.LanguageId==1
var selector2 = Expression.Lambda<Func<TT, bool>>(equalLanguage, arg2);
I couldn't combine "selector" and "selector2" with "where".
var lambda = Expression.Lambda<Func<T, IEnumerable<TT>>>(null, arg);
return lambda;
}
I solved the problem with my own means
public interface ITEntity<TT>
{
public List<TT> Translate { get; set; }
}
public interface ILEntity
{
public int LanguageId { get; set; }
}
public class FoodMenu : ITEntity<FoodMenuT>
{
public virtual List<FoodMenuT> Translate { get; set; } = new();
}
public class FoodMenuT : ILEntity
{
public int LanguageId { get; set; }
}
public static Expression<Func<T, IEnumerable<TT>>> LanguageFilter<T, TT>() where T : ITEntity<TT> where TT : ILEntity
{
return e => e.Translate.Where(x => x.LanguageId == 1);
}
I have the following classes:
public class Vehicle
{
public string Name { get; set; }
public bool CanFly { get; set; }
public bool CanDive { get; set; }
}
public class Hdd
{
public string Name { get; set; }
public bool CanRead { get; set; }
public bool CanWrite { get; set; }
public bool CanCopy { get; set; }
}
I want to write one function that can filter for example if a specific car exists (filter by firstOrDefault name) then check the given condition as parameter, for example CanFly or CanDive... etc
so i was thinking of:
public class TestProperties
{
public bool Check<T>(List<T> items, string name,
Expression<Func<T, bool>> expression)
{
var expr = (MemberExpression)expression.Body;
var prop = (PropertyInfo)expression.Member;
//1- Filter items with the given name
// return false if no records found
// return false if the condition fails
}
}
Then I would call the functions as follow
var myHdds= GetHdd();
var myCars= GetCars();
var CanRead = Check<Hdd>(myHdds,"samsung",x => x.CanRead);
var CanFly = Check<Vehicle>(myCars,"Audi",x => x.CanFly);
How can i implement the Check function?
You are almost there. Try this -
public bool Check<T>(List<T> items, Expression<Func<T, bool>> expression)
{
return items.Any(x => expression.Compile()(x));
}
or,
public bool Check<T>(List<T> items, Func<T, bool> compiledExp)
{
return items.Any(x => compiledExp(x));
}
and call like this -
Check<Vehicle>(myCars, x => x.Name == "Audi" && x.CanFly);
Check<Hdd>(myHdds,x => x.Name == "Samsung" && x.CanRead);
But now to think of this, you really don't need a method to do that. It is literally one line of code.
Personally I would have preferred an extension method -
public static bool Check<T>(this List<T> items, Func<T, bool> compiledExp)
{
return items.Any(x => compiledExp(x));
}
and call like -
myHdds.Check(x => x.Name == "Samsung" && x.CanRead);
I am currently trying to dynamically build linq queries. I want to be able to reuse Linq expressions in other Linq expressions.
For example:
public class Dummy
{
public string Test { get; set; }
public Sub Sub { get; set; }
}
public class Sub
{
public static Expression<Func<Sub, string>> Converter
{
get
{
return x => x.Id + ": " + x.Text;
}
}
public int Id { get; set; }
public string Text { get; set; }
}
When writing the converter for the Dummy class, the converter should be able to reuse the Sub.Converter. For thi spurpose I have written a DynamicSelect<> extension method:
var result = Query
.Where(x=>x.Sub != null)
.DynamicSelect(x => new Result())
.Select(x => x.SubText, x => x.Sub,Sub.Converter)
.Apply()
.ToList();
DynamicSelect creates a new SelectionBuilder,
the select part takes as first input the targetproperty (Result.SubText),
as second property the input property which we want to convert and as third input the converter for the Sub Property.
The .Apply call then returns the builtup expression tree.
I managed to get it working for a simpler usecase (without the subpath):
var result = Query.DynamicSelect(x => new Result())
.Select(x => x.ResolvedTest, x => x.Inner == null ? x.Test : x.Inner.Test)
.Select(x => x.SubText, x => x.Sub == null ? null : x.Sub.Text)
.Apply()
.ToList();
But how do I rebase an expression to another subpath?
Code so far:
public static class SelectBuilder
{
public static LinqDynamicConverter<TInput,TOutput> DynamicSelect<TInput,TOutput>(
this IQueryable<TInput> data,
Expression<Func<TInput, TOutput>> initialConverter) where TOutput : new()
{
return new LinqDynamicConverter<TInput,TOutput>(data, initialConverter);
}
}
public class LinqDynamicConverter<TInput,TOutput> where TOutput: new()
{
#region inner classes
private class MemberAssignmentVisitor : ExpressionVisitor
{
private IDictionary<MemberInfo, MemberAssignment> SinglePropertyToBinding { get; set; }
public MemberAssignmentVisitor(IDictionary<MemberInfo, MemberAssignment> singlePropertyToBinding)
{
SinglePropertyToBinding = singlePropertyToBinding;
}
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
{
SinglePropertyToBinding[node.Member] = node;
return base.VisitMemberAssignment(node);
}
}
private class MemberInfoVisitor : ExpressionVisitor
{
internal MemberInfo SingleProperty { get; private set; }
public MemberInfoVisitor()
{
}
protected override Expression VisitMember(MemberExpression node)
{
SingleProperty = node.Member;
return base.VisitMember(node);
}
}
#endregion
#region properties
private IQueryable<TInput> Data { get;set; }
private Expression<Func<TInput, TOutput>> InitialConverter { get;set;}
private IDictionary<MemberInfo, MemberAssignment> SinglePropertyToBinding { get; set; }
#endregion
#region constructor
internal LinqDynamicConverter(IQueryable<TInput> data,
Expression<Func<TInput, TOutput>> initialConverter)
{
Data = data;
InitialConverter = x => new TOutput(); // start with a clean plate
var replace = initialConverter.Replace(initialConverter.Parameters[0], InitialConverter.Parameters[0]);
SinglePropertyToBinding = new Dictionary<MemberInfo, MemberAssignment>();
MemberAssignmentVisitor v = new MemberAssignmentVisitor(SinglePropertyToBinding);
v.Visit(initialConverter);
}
#endregion
public LinqDynamicConverter<TInput,TOutput> Select<TProperty,TConverted>(
Expression<Func<TOutput, TConverted>> initializedOutputProperty,
Expression<Func<TInput, TProperty>> subPath,
Expression<Func<TProperty, TConverted>> subSelect)
{
//????
return this;
}
// this one works
public LinqDynamicConverter<TInput,TOutput> Select<TProperty>(
Expression<Func<TOutput, TProperty>> initializedOutputProperty,
Expression<Func<TInput, TProperty>> subSelect)
{
var miv = new MemberInfoVisitor();
miv.Visit(initializedOutputProperty);
var mi = miv.SingleProperty;
var param = InitialConverter.Parameters[0];
Expression<Func<TInput, TProperty>> replace = (Expression<Func<TInput, TProperty>>)subSelect.Replace(subSelect.Parameters[0], param);
var bind = Expression.Bind(mi, replace.Body);
SinglePropertyToBinding[mi] = bind;
return this;
}
public IQueryable<TOutput> Apply()
{
var converter = Expression.Lambda<Func<TInput, TOutput>>(
Expression.MemberInit((NewExpression)InitialConverter.Body, SinglePropertyToBinding.Values), InitialConverter.Parameters[0]);
return Data.Select(converter);
}
}
Found the solution. I needed to write a new Expression visitor that added the extra member acces(es):
/// <summary>
/// rebinds a full expression tree to a new single property
/// Example: we get x => x.Sub as subPath. Now the visitor starts visiting a new
/// expression x => x.Text. The result should be x => x.Sub.Text.
/// Traversing member accesses always starts with the rightmost side working toward the parameterexpression.
/// So when we reach the parameterexpression we have to inject the whole subpath including the parameter of the subpath
/// </summary>
/// <typeparam name="TConverted"></typeparam>
/// <typeparam name="TProperty"></typeparam>
private class LinqRebindVisitor<TConverted, TProperty> : ExpressionVisitor
{
public Expression<Func<TInput, TConverted>> ResultExpression { get; private set; }
private ParameterExpression SubPathParameter { get; set; }
private Expression SubPathBody { get; set; }
private bool InitialMode { get; set; }
public LinqRebindVisitor(Expression<Func<TInput, TProperty>> subPath)
{
SubPathParameter = subPath.Parameters[0];
SubPathBody = subPath.Body;
InitialMode = true;
}
protected override Expression VisitMember(MemberExpression node)
{
// Note that we cannot overwrite visitparameter because that method must return a parameterexpression
// So whenever we detect that our next expression will be a parameterexpression, we inject the subtree
if (node.Expression is ParameterExpression && node.Expression != SubPathParameter)
{
var expr = Visit(SubPathBody);
return Expression.MakeMemberAccess(expr, node.Member);
}
return base.VisitMember(node);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
bool initialMode = InitialMode;
InitialMode = false;
Expression<T> expr = (Expression<T>)base.VisitLambda<T>(node);
if (initialMode)
{
ResultExpression = Expression.Lambda<Func<TInput, TConverted>>(expr.Body,SubPathParameter);
}
return expr;
}
}
The single property Select method is then rather trivial:
public LinqDynamicConverter<TInput,TOutput> Select<TProperty,TConverted>(
Expression<Func<TOutput, TConverted>> initializedOutputProperty,
Expression<Func<TInput, TProperty>> subPath,
Expression<Func<TProperty, TConverted>> subSelect)
{
LinqRebindVisitor<TConverted, TProperty> rebindVisitor = new LinqRebindVisitor<TConverted, TProperty>(subPath);
rebindVisitor.Visit(subSelect);
var result = rebindVisitor.ResultExpression;
return Property<TConverted>(initializedOutputProperty, result);
}
We have an entity of type T1 which has a member of type T.
something like this :
public class T1
{
public T Member{get;set;}
}
User can use our UI to give us a filter over T and we have translate it to an expression of a function that gets a T and returns bool (Expression<Func<T,bool>>)
I would like to know is it possible to convert this to an expression of a function that gets T1 and returns bool.
Actually I'd like to convert this :
(t=>t.Member1==someValue && t.Member2==someOtherValue);
to this :
(t1=>t1.Member.Member1==someValue && t1.Member.Member2==someOtherValue);
You can do it with a few way.
First and simplest: use Expression.Invoke
Expression<Func<T, bool>> exprT = t.Member1==someValue && t.Member2==someOtherValue
ParameterExpression p = Expression.Parameter(typeof(T1));
var expr = Expression.Invoke(expr, Expression.PropertyOrField(p, "Member"));
Expression<Func<T1, bool>> exprT1 = Expression.Lambda<Func<T1, bool>>(expr, p);
but in this case you get not
t1 => (t=>(t.Member1==someValue && t.Member2==someOtherValue))(t1.Member),
instead of
(t1=>t1.Member.Member1==someValue && t1.Member.Member2==someOtherValue);
For replacing you can use ExpressionVisitor class like
class V : ExpressionVisitor
{
public ParameterExpression Parameter { get; private set; }
Expression m;
public V(Type parameterType, string member)
{
Parameter = Expression.Parameter(parameterType);
this.m = Expression.PropertyOrField(Parameter, member);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == m.Type)
{
return m;
}
return base.VisitParameter(node);
}
}
and use it
var v = new V(typeof(T1), "Member");
var exprT1 = Expression.Lambda<Func<T1, bool>>(v.Visit(exprT.Body), v.Parameter);
Given
public class MyClass
{
public MyInner Member { get; set; }
}
public class MyInner
{
public string Member1 { get; set; }
public string Member2 { get; set; }
}
plus
public static Expression<Func<TOuter, bool>> Replace<TOuter, TInner>(Expression<Func<TInner, bool>> exp, Expression<Func<TOuter, TInner>> outerToInner)
{
var body2 = new ExpressionReplacer { From = exp.Parameters[0], To = outerToInner.Body }.Visit(exp.Body);
var lambda2 = Expression.Lambda<Func<TOuter, bool>>(body2, outerToInner.Parameters);
return lambda2;
}
and
public class ExpressionReplacer : ExpressionVisitor
{
public Expression From;
public Expression To;
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == From)
{
return base.Visit(To);
}
return base.VisitParameter(node);
}
}
you can
// The initial "data"
string someValue = "Foo";
string someOtherValue = "Bar";
Expression<Func<MyInner, bool>> exp = t => t.Member1 == someValue && t.Member2 == someOtherValue;
Expression<Func<MyClass, MyInner>> outerToInner = u => u.Member;
// The "new" expression
Expression<Func<MyClass, bool>> result = Replace(exp, outerToInner);
The ExpressionReplacer class replaces a parameter of an expression with another expression, while the Replace method uses the ExpressionReplacer and then rebuilds a new expression.
I have an Expression where I like to insert one more node. I found this SO question similar question which adds a Property at the end of an Expression.
I'm quite new to Expressions and I can't figure out to do it between nodes.
I did this simple console test application to show the problem I have:
public class Program
{
private static void Main()
{
var provider = new Provider<Foo>();
var foo = new Foo {Label = "myLabel", Owner = "Me"};
provider.AddData(foo);
provider.AddExpression(f => f.Label);
// This writes: f => f.Label
Console.WriteLine(provider.OriginalExpression.ToString());
// This should write: f => f.WrappedData.Label
// At the moment it writes: NULL
Console.WriteLine(provider.WrapperExpression == null ? "NULL" : provider.WrapperExpression.ToString());
Console.ReadKey();
}
}
public class Foo
{
public string Label { get; set; }
public string Owner { get; set; }
}
public class Wrapper<T> where T : class
{
public string Id { get; set; }
public T WrappedData { get; set; }
}
public class Provider<T> where T : class
{
public Wrapper<T> _internalWrapper = new Wrapper<T>();
// The expression for the Property when using T (i.e. Foo)
public Expression<Func<T, string>> OriginalExpression { get; private set; }
// The expression for the Property when using _internalWrapper
public Expression<Func<Wrapper<T>, string>> WrapperExpression { get; private set; }
public void AddData(T data)
{
_internalWrapper = new Wrapper<T> { WrappedData = data, Id = "myId" };
}
public void AddExpression(Expression<Func<T, string>> valueExpression)
{
OriginalExpression = valueExpression;
BuildWrapperExpression();
}
private void BuildWrapperExpression()
{
// HERE IS THE PROBLEM:
// Here I have to insert the node "WrappedData" in the OriginalExpression and save it as WrapperExpression
// So {f => f.Label} must result into {f => f.WrappedData.Label}
// It should work as well for deeper nodes. I.e. when OriginalExpression is something like {f => f.Bar.Prop2.Label}
}
}
I've already tried a view versions in the BuildWrapperExpression() method but none delivers f => f.WrappedData.Label.
I get something like
f => Invoke(value (ConsoleApplication1.Provide1+<>c__DisplayClass1[ConsoleApplication1.Foo]).lambda, f.WrappedData) or
x => Invoke(f => f.Label, Invoke(e => e.WrappedData, x))
For my further use of the expression is has to be x => x.WrappedData.Label
Thanks a lot guys
Can't you simply:
private void BuildWrapperExpression()
{
var lambda = OriginalExpression.Compile();
WrapperExpression = x => lambda(x.WrappedData);
}
Alternatively, you can implement a ExpressionVistor. Check Marc's answer: https://stackoverflow.com/a/9132775/1386995
I came up with a great solution using the link Nikita provided.
I'm using this new class ExpressionChainer:
public static class ExpressionChainer
{
public static Expression<Func<TOuter, TInner>> Chain<TOuter, TMiddle, TInner>(
this Expression<Func<TOuter, TMiddle>> left, Expression<Func<TMiddle, TInner>> right)
{
return ChainTwo(left, right);
}
public static Expression<Func<TOuter, TInner>> ChainTwo<TOuter, TMiddle, TInner>(
Expression<Func<TOuter, TMiddle>> left, Expression<Func<TMiddle, TInner>> right)
{
var swap = new SwapVisitor(right.Parameters[0], left.Body);
return Expression.Lambda<Func<TOuter, TInner>>(
swap.Visit(right.Body), left.Parameters);
}
private class SwapVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public SwapVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
}
Then all I have to do in is:
private void BuildWrapperExpression()
{
Expression<Func<Wrapper<T>, T>> expression = x => x.WrappedData;
WrapperExpression = expression.Chain(OriginalExpression);
}
Which gives me x => x.WrappedData.Label as WrapperExpression