Combine several similar SELECT-expressions into a single expression - c#

How to combine several similar SELECT-expressions into a single expression?
private static Expression<Func<Agency, AgencyDTO>> CombineSelectors(params Expression<Func<Agency, AgencyDTO>>[] selectors)
{
// ???
return null;
}
private void Query()
{
Expression<Func<Agency, AgencyDTO>> selector1 = x => new AgencyDTO { Name = x.Name };
Expression<Func<Agency, AgencyDTO>> selector2 = x => new AgencyDTO { Phone = x.PhoneNumber };
Expression<Func<Agency, AgencyDTO>> selector3 = x => new AgencyDTO { Location = x.Locality.Name };
Expression<Func<Agency, AgencyDTO>> selector4 = x => new AgencyDTO { EmployeeCount = x.Employees.Count() };
using (RealtyContext context = Session.CreateContext())
{
IQueryable<AgencyDTO> agencies = context.Agencies.Select(CombineSelectors(selector3, selector4));
foreach (AgencyDTO agencyDTO in agencies)
{
// do something..;
}
}
}

Not simple; you need to rewrite all the expressions - well, strictly speaking you can recycle most of one of them, but the problem is that you have different x in each (even though it looks the same), hence you need to use a visitor to replace all the parameters with the final x. Fortunately this isn't too bad in 4.0:
static void Main() {
Expression<Func<Agency, AgencyDTO>> selector1 = x => new AgencyDTO { Name = x.Name };
Expression<Func<Agency, AgencyDTO>> selector2 = x => new AgencyDTO { Phone = x.PhoneNumber };
Expression<Func<Agency, AgencyDTO>> selector3 = x => new AgencyDTO { Location = x.Locality.Name };
Expression<Func<Agency, AgencyDTO>> selector4 = x => new AgencyDTO { EmployeeCount = x.Employees.Count() };
// combine the assignments from the 4 selectors
var convert = Combine(selector1, selector2, selector3, selector4);
// sample data
var orig = new Agency
{
Name = "a",
PhoneNumber = "b",
Locality = new Location { Name = "c" },
Employees = new List<Employee> { new Employee(), new Employee() }
};
// check it
var dto = new[] { orig }.AsQueryable().Select(convert).Single();
Console.WriteLine(dto.Name); // a
Console.WriteLine(dto.Phone); // b
Console.WriteLine(dto.Location); // c
Console.WriteLine(dto.EmployeeCount); // 2
}
static Expression<Func<TSource, TDestination>> Combine<TSource, TDestination>(
params Expression<Func<TSource, TDestination>>[] selectors)
{
var zeroth = ((MemberInitExpression)selectors[0].Body);
var param = selectors[0].Parameters[0];
List<MemberBinding> bindings = new List<MemberBinding>(zeroth.Bindings.OfType<MemberAssignment>());
for (int i = 1; i < selectors.Length; i++)
{
var memberInit = (MemberInitExpression)selectors[i].Body;
var replace = new ParameterReplaceVisitor(selectors[i].Parameters[0], param);
foreach (var binding in memberInit.Bindings.OfType<MemberAssignment>())
{
bindings.Add(Expression.Bind(binding.Member,
replace.VisitAndConvert(binding.Expression, "Combine")));
}
}
return Expression.Lambda<Func<TSource, TDestination>>(
Expression.MemberInit(zeroth.NewExpression, bindings), param);
}
class ParameterReplaceVisitor : ExpressionVisitor
{
private readonly ParameterExpression from, to;
public ParameterReplaceVisitor(ParameterExpression from, ParameterExpression to)
{
this.from = from;
this.to = to;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node == from ? to : base.VisitParameter(node);
}
}
This uses the constructor from the first expression found, so you might want to sanity-check that all of the others use trivial constructors in their respective NewExpressions. I've left that for the reader, though.
Edit: In the comments, #Slaks notes that more LINQ could make this shorter. He is of course right - a bit dense for easy reading, though:
static Expression<Func<TSource, TDestination>> Combine<TSource, TDestination>(
params Expression<Func<TSource, TDestination>>[] selectors)
{
var param = Expression.Parameter(typeof(TSource), "x");
return Expression.Lambda<Func<TSource, TDestination>>(
Expression.MemberInit(
Expression.New(typeof(TDestination).GetConstructor(Type.EmptyTypes)),
from selector in selectors
let replace = new ParameterReplaceVisitor(
selector.Parameters[0], param)
from binding in ((MemberInitExpression)selector.Body).Bindings
.OfType<MemberAssignment>()
select Expression.Bind(binding.Member,
replace.VisitAndConvert(binding.Expression, "Combine")))
, param);
}

If all of the selectors will only initialize AgencyDTO objects (like your example), you can cast the expressions to NewExpression instances, then call Expression.New with the Members of the expressions.
You'll also need an ExpressionVisitor to replace the ParameterExpressions from the original expressions with a single ParameterExpression for the expression you're creating.

In case anyone else stumbles upon this with a similar use case as mine (my selects targeted different classes based on the level of detail needed):
Simplified scenario:
public class BlogSummaryViewModel
{
public string Name { get; set; }
public static Expression<Func<Data.Blog, BlogSummaryViewModel>> Map()
{
return (i => new BlogSummaryViewModel
{
Name = i.Name
});
}
}
public class BlogViewModel : BlogSummaryViewModel
{
public int PostCount { get; set; }
public static Expression<Func<Data.Blog, BlogViewModel>> Map()
{
return (i => new BlogViewModel
{
Name = i.Name,
PostCount = i.Posts.Count()
});
}
}
I adapted the solution provided by #Marc Gravell like so:
public static class ExpressionMapExtensions
{
public static Expression<Func<TSource, TTargetB>> Concat<TSource, TTargetA, TTargetB>(
this Expression<Func<TSource, TTargetA>> mapA, Expression<Func<TSource, TTargetB>> mapB)
where TTargetB : TTargetA
{
var param = Expression.Parameter(typeof(TSource), "i");
return Expression.Lambda<Func<TSource, TTargetB>>(
Expression.MemberInit(
((MemberInitExpression)mapB.Body).NewExpression,
(new LambdaExpression[] { mapA, mapB }).SelectMany(e =>
{
var bindings = ((MemberInitExpression)e.Body).Bindings.OfType<MemberAssignment>();
return bindings.Select(b =>
{
var paramReplacedExp = new ParameterReplaceVisitor(e.Parameters[0], param).VisitAndConvert(b.Expression, "Combine");
return Expression.Bind(b.Member, paramReplacedExp);
});
})),
param);
}
private class ParameterReplaceVisitor : ExpressionVisitor
{
private readonly ParameterExpression original;
private readonly ParameterExpression updated;
public ParameterReplaceVisitor(ParameterExpression original, ParameterExpression updated)
{
this.original = original;
this.updated = updated;
}
protected override Expression VisitParameter(ParameterExpression node) => node == original ? updated : base.VisitParameter(node);
}
}
The Map method of the extended class then becomes:
public static Expression<Func<Data.Blog, BlogViewModel>> Map()
{
return BlogSummaryViewModel.Map().Concat(i => new BlogViewModel
{
PostCount = i.Posts.Count()
});
}

Related

Make delegate for method at runtime

I found this interesting article Reflection Performance - Create Delegate (Properties C#)
the described approach works great for properties. So I tried to to make it work for Methods, too, but without success.
Classes / Properties / Methods
public class bmecatContent
{
private bmecatHeader header;
private bmecatCatalog catalog;
private List<bmecatFieldValue> fieldValueList;
public bmecatContent()
{
header = new bmecatHeader();
catalog = new bmecatCatalog();
}
public string DeclarationVersion { get; set; }
public string DeclarationEncoding { get; set; }
public string BmecatVersion { get; set; }
public bmecatHeader Header
{ get { return header; } }
public bmecatCatalog Catalog
{ get { return catalog; } }
}
public class bmecatCatalog
{
private List<bmecatCatalogGroupSystem> catalogGroupSystem;
private List<bmecatClassificationSystem> classificationSystem;
private List<bmecatProduct> products;
private List<bmecatProductToCataloggroupMap> productToCataloggroupMap;
public bmecatCatalog()
{
catalogGroupSystem = new List<bmecatCatalogGroupSystem>();
classificationSystem = new List<bmecatClassificationSystem>();
products = new List<bmecatProduct>();
productToCataloggroupMap = new List<bmecatProductToCataloggroupMap>();
}
public List<bmecatClassificationSystem> Classification_System
{ get { return classificationSystem; } }
public List<bmecatCatalogGroupSystem> Catalog_Group_System
{ get { return catalogGroupSystem; } }
public List<bmecatProduct> Products
{ get { return products; } }
public List<bmecatProductToCataloggroupMap> Product_To_Cataloggroup_Map
{ get { return productToCataloggroupMap; } }
public bmecatProduct GetProductByInernationalPid(string Pid)
{
// linq
var query = from prodItem in products
from innerList in prodItem.Product_Details.International_PID
where innerList.PID == Pid
select prodItem;
return query.FirstOrDefault();
}
}
my current Approach looks like:
// Properties
public static Func<object, object> BuildGetAccessor(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
Expression<Func<object, object>> expr =
Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method),
typeof(object)),
obj);
return expr.Compile();
}
// Methods (with string Parameter)
public static Func<object, string, object> BuildMethodAccessor(MethodInfo method)
{
var obj = Expression.Parameter(typeof(object), "o");
var strParam = Expression.Parameter(typeof(string), "strParam");
//var param = method.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).FirstOrDefault();
var param = Expression.Convert(strParam, method.GetParameters().First().ParameterType);
Expression<Func<object, string, object>> expr =
Expression.Lambda<Func<object, string, object>>(
Expression.Convert(Expression.Call(Expression.Convert(obj, method.DeclaringType), method, param),
typeof(object)),
obj);
return expr.Compile();
}
this code generates messages, that for the lambda-declaration a wrong number of Parameters was used.
thx a lot for your help!
// Update
this is my "work in progress" part when it Comes to creating & using the delegates:
bmecatParser parser = new bmecatParser();
// parser contains Property BmecatContent of type bmecatContent
// BmecatContent contains all properties and Methods I Need to Access at runtime
// e.g. BmecatContent.Catalog, BmecatContent.Catalog.GetProductByInernationalPid(string Pid)
// gets instance of main-class
var property = parser.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "BmecatContent");
var access = Extensions.BuildGetAccessor(property.GetGetMethod());
var resultBmecatContent = access(parser);
// gets instance of class that holds method
property = resultBmecatContent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "Catalog");
access = Extensions.BuildGetAccessor(property.GetGetMethod());
var resultCatalog = access(resultBmecatContent);
// here I try to get value from method that has 1 Parameter (string)
var method = resultCatalog.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "GetProductByInernationalPid");
var accessProd = Extensions.BuildMethodAccessor(method);
var resultProduct = accessProd(resultCatalog, "4317784548366");
the idea behind this is to parse given classes + properties structure, where user provides propertynames / methodnames within mappinginstructions.

Create predicate with a BinaryExpression containing multiple parameters

is it possible to dynamically generate such a predicate using LambdaExpressions?
Expression<Func<Test, bool>> predicate = t =>
t.Levels.Any(l =>
l.LevelDetails.Any( ld =>
ld.LevelDate > DbFunctions.AddDays(t.TestDate, 1)
)
);
As long as the parameters in the inner BinaryExpression are identical or the right part of the expression is constant, there is no problem. But the example expressionld.LevelDate > DbFunctions.AddDays (t.TestDate, 1) contains two different ExpressionParameters which are independent from each other. What I am looking for is something like this:
Expression<Func<LevelDetail, DateTime?>> left =
ld => ld.LevelDate;
Expression<Func<Test, DateTime?>> right =
t => DbFunctions.AddDays(t.TestDate, 1);
BinaryExpression expr =
Expression.GreaterThan(
((LambdaExpression)left).Body,
((LambdaExpression)right).Body
);
Expression<Func<Test, bool>> predicate = t =>
t.Levels.Any(l =>
l.LevelDetails.Any( **expr** )
);
class Test {
public DateTime TestDate { get; set; }
public virtual ICollection<Level> Levels { get; set; }
}
class Level {
public virtual ICollection<LevelDetail> LevelDetails { get; set; }
}
class LevelDetail {
public DateTime LevelDate { get; set; }
}
Kind regards!
As pointed out in the answers by #Matt Warren if yow want to combine lambdas you will need to do it by hand and will need to set the correct expression parameters.
Firstlly, you will need a ExpressionVisitor that can replace node that you want:
private class SwapVisitor : ExpressionVisitor
{
public readonly Expression _from;
public readonly Expression _to;
public SwapVisitor(Expression from, Expression to)
{
_from = from;
_to = to;
}
public override Expression Visit(Expression node) => node == _from ? _to : base.Visit(node);
}
Secondly, you will need to combine lambdas by hand:
private static Expression<Func<Test, bool>> CreatePredicate()
{
Expression<Func<LevelDetail, DateTime?>> left = ld => ld.LevelDate;
// I didn't include EF, so I did test it just use directly Test.TestDate
//Expression<Func<Test, DateTime?>> right = t => t.TestDate;
Expression<Func<Test, DateTime?>> right = t => DbFunctions.AddDays(t.TestDate, 1);
var testParam = Expression.Parameter(typeof(Test), "test_par");
var levelParam = Expression.Parameter(typeof(Level), "level_par");
var detailParam = Expression.Parameter(typeof(LevelDetail), "detail_par");
// Swap parameters for right and left operands to the correct parameters
var swapRight = new SwapVisitor(right.Parameters[0], testParam);
right = swapRight.Visit(right) as Expression<Func<Test, DateTime?>>;
var swapLeft = new SwapVisitor(left.Parameters[0], detailParam);
left = swapLeft.Visit(left) as Expression<Func<LevelDetail, DateTime?>>;
BinaryExpression comparer = Expression.GreaterThan(left.Body, right.Body);
var lambdaComparer = Expression.Lambda<Func<LevelDetail, bool>>(comparer, detailParam);
// Well, we created here the lambda for ld => ld.LevelDate > DbFunctions.AddDays(t.TestDate, 1)
var anyInfo = typeof(Enumerable).GetMethods().Where(info => info.Name == "Any" && info.GetParameters().Length == 2).Single();
// Will create **l.LevelDetails.Any(...)** in the code below
var anyInfoDetail = anyInfo.MakeGenericMethod(typeof(LevelDetail));
var anyDetailExp = Expression.Call(anyInfoDetail, Expression.Property(levelParam, "LevelDetails"), lambdaComparer);
var lambdaAnyDetail = Expression.Lambda<Func<Level, bool>>(anyDetailExp, levelParam);
// Will create **t.Levels.Any(...)** in the code below and will return the finished lambda
var anyInfoLevel = anyInfo.MakeGenericMethod(typeof(Level));
var anyLevelExp = Expression.Call(anyInfoLevel, Expression.Property(testParam, "Levels"), lambdaAnyDetail);
var lambdaAnyLevel = Expression.Lambda<Func<Test, bool>>(anyLevelExp, testParam);
return lambdaAnyLevel;
}
And the code below contains usage of this:
var predicate = CreatePredicate();
var levelDetail = new LevelDetail { LevelDate = new DateTime(2017, 08, 19) };
var level = new Level { LevelDetails = new List<LevelDetail> { levelDetail } };
var test = new Test { TestDate = new DateTime(2027, 08, 19), Levels = new List<Level> { level } };
var result = predicate.Compile()(test);
I would recommend using nein-linq to combine, build and compose predicates (and many other expression puzzles),
or LinqKit
Both support Entity Framework
For example, using nein-linq
Given:
public static class TestExpressions
{
[InjectLambda]
public static bool IsTestDateEarlierThan(this Test test, DateTime? dateTime, int numberOfDays)
{
return dateTime > test.TestDate.AddDays(numberOfDays);
}
public static Expression<Func<Test, DateTime?, int, bool>> IsTestDateEarlierThan()
{
return (test, dateTime, numberOfDays) => dateTime > DbFunctions.AddDays(test.TestDate, numberOfDays);
}
// Simple caching...
private static readonly Func<Test, int, bool> _hasAnyLevelDateAfterTestDays = HasAnyLevelDateAfterTestDays().Compile();
[InjectLambda]
public static bool HasAnyLevelDateAfterTestDays(this Test test, int numberOfDays)
{
return _hasAnyLevelDateAfterTestDays(test, numberOfDays);
}
public static Expression<Func<Test, int, bool>> HasAnyLevelDateAfterTestDays()
{
return (test, numberOfDays) => test.Levels.Any(l => l.LevelDetails.Any(ld => test.IsTestDateEarlierThan(ld.LevelDate, numberOfDays)));
}
}
When:
var testList = new List<Test>
{
new Test {
Levels = new List<Level> {
new Level {
LevelDetails = new List<LevelDetail> {
new LevelDetail {
LevelDate = DateTime.Today
}
}
}
},
// Not matched
TestDate = DateTime.Today
},
new Test {
Levels = new List<Level> {
new Level {
LevelDetails = new List<LevelDetail> {
new LevelDetail {
LevelDate = DateTime.Today
}
}
}
},
// Not matched
TestDate = DateTime.Today.AddDays(-1)
},
new Test {
Levels = new List<Level> {
new Level {
LevelDetails = new List<LevelDetail> {
new LevelDetail {
LevelDate = DateTime.Today
}
}
}
},
// Matched
TestDate = DateTime.Today.AddDays(-2)
}
};
Then:
var testQuery = testList.AsQueryable();
// Alternative one
var result1 = testQuery
.ToInjectable() // Don't forget!!
.Where(test => test.Levels.Any(l => l.LevelDetails.Any(ld => test.IsTestDateEarlierThan(ld.LevelDate, 1))))
.ToList();
// Alternative two: You get the point :)
var result2 = testQuery
.ToInjectable() // Don't forget!!
.Where(test => test.HasAnyLevelDateAfterTestDays(1))
.ToList();
When you build an expression with nested lambda's the inner lambda's expressions will be able to access the outer lambda's parameters. It works the same way with Expression<T> lambdas as with regular C# lambdas.
If you are working with Expression<T> lambdas and trying to combine them, you'll need to work with them at the API level (do it by hand), and not expect the automatic C# language syntax to Expression<T> conversion to help you out.
One thing to note: when you created the two original lambdas (via conversion to Expression<T>), they each got their own ParameterExpression instances, which will make it impossible to combine them because both bodies will need to be referencing the same instance (unless you replace one for the other using an ExpressionVisitor.)

Combine multiple expression to create a Linq selector expression

I'm trying to dynamically build the select statement of a Linq query.
I have a function like this:
public Task<List<T>> RunQuery<T>(
IQueryable<T> query,
FieldSelector<T> fields,
int pageNumber, int pageSize)
{
var skip = (pageNumber-1) * pageSize;
var query = query.Skip(skip).Take(pageSize);
var selector = fields.GetSelector();
return query.Select(selector).ToListAsync();
}
Here is the FieldSelector class: (I my code I have extra properties per field)
public class FieldSelector<T>
{
private List<LambdaExpression> expressions;
public FieldSelector()
{
expressions = new List<LambdaExpression>();
}
public void Add(Expression<Func<T, object>> expr)
{
expressions.Add(expr);
}
public Expression<Func<T, object>> GetSelector()
{
//Build an expression like e => new {e.Name, e.Street}
}
}
How to implement the GetSelector function? Is it possible? (without getting too complex) .
This is how I would like to use it:
var fields = new FieldSelector<Employee>();
fields.Add(e => e.Name);
fields.Add(e => e.Street);
RunQuery<Employee>(query, fields, 1, 100);
You need to generate a custom type how that is doing the compiler for Anonymous Type, because you cannot generate the really Anonymous Type with dynamic properties count. After generation this type you can easy set the assignments of expressions that you passed to FieldSelector and combine it to custom type.
public class FieldSelector<T>
{
private List<LambdaExpression> expressions;
public FieldSelector()
{
expressions = new List<LambdaExpression>();
}
public void Add(Expression<Func<T, object>> expr)
{
expressions.Add(expr);
}
public Expression<Func<T, object>> GetSelector()
{
// We will create a new type in runtime that looks like a AnonymousType
var str = $"<>f__AnonymousType0`{expressions.Count}";
// Create type builder
var assemblyName = Assembly.GetExecutingAssembly().GetName();
var modelBuilder = AppDomain.CurrentDomain
.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect)
.DefineDynamicModule("module");
var typeBuilder = modelBuilder.DefineType(str, TypeAttributes.Public | TypeAttributes.Class);
var types = new Type[expressions.Count];
var names = new List<string>[expressions.Count];
for (int i = 0; i < expressions.Count; i++)
{
// Retrive passed properties
var unExpr = expressions[i].Body as UnaryExpression;
var exp = unExpr == null ? expressions[i].Body as MemberExpression : unExpr.Operand as MemberExpression;
types[i] = exp.Type;
// Retrive a nested properties
names[i] = GetAllNestedMembersName(exp);
}
// Defined generic parameters for custom type
var genericParams = typeBuilder.DefineGenericParameters(types.Select((_, i) => $"PropType{i}").ToArray());
for (int i = 0; i < types.Length; i++)
{
typeBuilder.DefineField($"{string.Join("_", names[i])}", genericParams[i], FieldAttributes.Public);
}
// Create generic type by passed properties
var type = typeBuilder.CreateType();
var genericType = type.MakeGenericType(types);
ParameterExpression parameter = Expression.Parameter(typeof(T), "MyItem");
// Create nested properties
var assignments = genericType.GetFields().Select((prop, i) => Expression.Bind(prop, GetAllNestedMembers(parameter, names[i])));
return Expression.Lambda<Func<T, object>>(Expression.MemberInit(Expression.New(genericType.GetConstructors()[0]), assignments), parameter);
}
private Expression GetAllNestedMembers(Expression parameter, List<string> properties)
{
Expression expression = parameter;
for (int i = 0; i < properties.Count; ++i)
{
expression = Expression.Property(expression, properties[i]);
}
return expression;
}
private List<string> GetAllNestedMembersName(Expression arg)
{
var result = new List<string>();
var expression = arg as MemberExpression;
while (expression != null && expression.NodeType != ExpressionType.Parameter)
{
result.Insert(0, expression.Member.Name);
expression = expression.Expression as MemberExpression;
}
return result;
}
}
By the way, as you saw in the code above current solution doesn't work for cases when you try to retrieve property with more than 1 level nesting class1->class2->class3->Propery_1. It's not hard to fix it.
EDIT: Case above was fixed. Now you can retrive class1->class2->class3->Propery_1
And it's usage of it:
private class TestClass
{
public string Arg2 { get; set; }
public TestClass Nested { get; set; }
public int Id { get; set; }
}
var field = new FieldSelector<TestClass>();
field.Add(e => e.Arg2);
field.Add(e => e.Id);
field.Add(e => e.Nested.Id);
dynamic cusObj = field.GetSelector().Compile()(new TestClass { Arg2 = "asd", Id = 6, Nested = new TestClass { Id = 79 } });
Console.WriteLine(cusObj.Arg2);
Console.WriteLine(cusObj.Id);
Console.WriteLine(cusObj.Nested_Id);
Unfortunately, cusObj will be object at compile type and you cannot retrieve his propertis if you doesn't mark it as dynamic.
By the way, your public Task<List<T>> RunQuery<T> wouldn't be compile, because field.GetSelector() returns Func<T, object> and you will get a Task<List<object>> when you will invoke return return query.Select(field.GetSelector()).ToListAsync()
Hope, this is helpful.

Expression.Call and Count

I'm looking for a way to do following dynamically:
var q = context.Subscription
.Include("Client")
.Include("Invoices")
Where(s=>s.Client.Invoices.Count(i=>i.InvoiceID == SomeInt) > 0);
I would like to build expression dynamically for the left side:
Expression left = s => s.Client.Invoices.Count(i => i.InvoiceID == iSomeVar); //!
Expression right = Expression.Constant(0);
var binary = Expression.GreaterThan(left, right);
Thanks!
UPDATED NOTES:
Please note: The end result must be
Expression<Func<T, bool>>
Simple version:
// To give clear idea, all what I want to achieve is to determine
// whether specific record exists in reference table using known Path.
// Ultimately I want to extend following function (which works great by
// the way, but for simple operations)
static Expression CreateExpression<T>(string propertyPath,
object propertyValue,
ParameterExpression parameterExpression)
{
PropertyInfo property = typeof(T).GetProperty(propertyName);
MemberExpression left = Expression.Property(parameterExpression, property);
ConstantExpression right = Expression.Constant(0);
BinaryExpression binary = Expression.GreaterThan(left, right);
return binary;
}
// And I want to call this function and get result exactly as shown below:
Expression result =
CreateExpression<Subscription>("Client.Invoices.InvoiceID",
theID,
valueSelector.Parameters.Single());
// Where result will be:
// t => t.Client.Invoices.Count(i => i.InvoiceID == theID) > 0;
Extended version:
// 1) I'm using Silverlight 4, EF, RIA.
// 2) At the server side I have a function GetSubscriptionsByCriteria
// that looks about it:
public IQueryable<Subscription> GetSubscriptionsByCriteria(...)
{
var query = this.ObjectContext.Subscriptions.Include("Client")
.Include("Client.Invoices");
var criteria = BuildCriteria(...);
return query.Where(criteria)
}
// 3) BuildCriteria(...) function gathers Expressions and
// aggregates it into the single Expression with different
// AND/OR conditions, something like that:
public Expression<Func<Subscription, bool>> BuildCriteria(
List<SearchFilter> filters,
Expression<Func<Subscription, bool>> valueSelector)
{
List<Expression> filterExpressions = new List<Expression>();
...
Expression expr = CreateExpression<Subscription>(
sfItem.DBPropertyName,
sfItem.DBPropertyValue,
paramExpression,
sf.SearchCondition);
filterExpressions.Add(expr);
...
var filterBody =
filterExpressions.Aggregate<Expression>(
(accumulate, equal) => Expression.And(accumulate, equal));
return Expression
.Lambda<Func<Subscription, bool>>(filterBody, paramExpression);
}
// 4) Here is the simplified version of CreateExpression function:
static Expression CreateExpression<T>(string propertyName,
object propertyValue,
ParameterExpression paramExpression)
{
PropertyInfo property = typeof(T).GetProperty(propertyName);
ConstantExpression right = Expression.Constant(0);
MemberExpression left = Expression.Property(paramExpression, property);
return binary = Expression.Equals(left, right);
}
So, I hope it's clear now why do I need Expression for the left side in my original post. Trying to make this as DRY as possible.
P.S. Not to make it too confusing here is why I think I need to do ёExpression.Call(...)ё:
When I run following code and break it to see DebugView I notice this:
Expression<Func<Subscription, bool>> predicate =
t => t.Client.Invoices.Count(i => i.InvoiceID == 5) > 0;
BinaryExpression eq = (BinaryExpression)predicate.Body;
var left = eq.Left; // <-- See DEBUG VIEW
var right = eq.Right;
// DEBUG VIEW:
// Arguments: Count = 2
// [0] = {t.Client.Invoices}
// [1] = {i => (i.InvoiceID == 5)}
// DebugView: ".Call System.Linq.Enumerable.Count(
// ($t.Client).ClientInvoices,
// .Lambda#Lambda1<System.Func`2[SLApp.Web.Invoice,System.Boolean]>)
// .Lambda#Lambda1<System.Func`2[SLApp.Web.Invoice,System.Boolean]>
// (SLApp.Web.ClientInvoice $i){ $i.ClientInvoiceID == 5 }"
Here's a working program that does what I think you'd like. It defines a function that takes a path to an integer property inside a collection, and an integer value. It then checks whether or not that collection has Count > 0 of that value.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections;
namespace Test_Console
{
public class Subscription
{
public int Id { get; set; }
public Client Client { get; set; }
}
public class Client
{
public ICollection<Invoice> Invoices { get; set; }
}
public class Invoice
{
public int Id { get; set; }
}
class Program
{
static void Main(string[] args)
{
var subscriptions = new[]
{
new Subscription { Id = 1, Client = new Client { Invoices = new [] {
new Invoice { Id = 1 },
new Invoice { Id = 2 },
new Invoice { Id = 5 }
} } },
new Subscription { Id = 2, Client = new Client { Invoices = new [] {
new Invoice { Id = 4 },
new Invoice { Id = 5 },
new Invoice { Id = 5 }
} } },
new Subscription { Id = 3, Client = new Client { Invoices = new Invoice[] {
} } },
};
var propertyPath = "Client.Invoices.Id";
Console.WriteLine("What Id would you like to check " + propertyPath + " for?");
var propertyValue = int.Parse(Console.ReadLine());
var whereNumberOne = makeWhere<Subscription>(propertyPath, propertyValue);
Console.WriteLine("The following Subscription objects match:");
foreach (var s in subscriptions.Where(whereNumberOne).ToList())
{
Console.WriteLine("Id: " + s.Id);
}
}
private static Func<T, bool> makeWhere<T>(string propertyPath, int propertyValue)
{
string[] navigateProperties = propertyPath.Split('.');
var currentType = typeof(T);
var functoidChain = new List<Func<object, object>>();
functoidChain.Add(x => x); // identity function starts the chain
foreach (var nextProperty in navigateProperties)
{
// must be inside loop so the closer on the functoids works properly
PropertyInfo nextPropertyInfo;
if (currentType.IsGenericType
&& currentType.GetGenericTypeDefinition().GetInterfaces().Contains(typeof(IEnumerable)))
{
nextPropertyInfo = currentType.GetGenericArguments()[0].GetProperty(nextProperty);
functoidChain.Add(x =>
((IEnumerable<object>)x)
.Count(y => (int)nextPropertyInfo.GetValue(y, null) == propertyValue)
);
}
else
{
nextPropertyInfo = currentType.GetProperty(nextProperty);
functoidChain.Add(x => nextPropertyInfo.GetValue(x, null));
}
currentType = nextPropertyInfo.PropertyType;
}
// compose the functions together
var composedFunctoidChain = functoidChain.Aggregate((f, g) => x => g(f(x)));
var leftSide = new Func<T, int>(x => (int)composedFunctoidChain(x));
return new Func<T, bool>(r => leftSide(r) > 0);
}
}
}
I think this should get you closer to what you're going for:
static Expression<Func<T, bool>> CreateAnyExpression<T, T2>(string propertyPath,
Expression<Func<T2, bool>> matchExpression)
{
var type = typeof(T);
var parameterExpression = Expression.Parameter(type, "s");
var propertyNames = propertyPath.Split('.');
Expression propBase = parameterExpression;
foreach(var propertyName in propertyNames)
{
PropertyInfo property = type.GetProperty(propertyName);
propBase = Expression.Property(propBase, property);
type = propBase.Type;
}
var itemType = type.GetGenericArguments()[0];
// .Any(...) is better than .Count(...) > 0
var anyMethod = typeof(Enumerable).GetMethods()
.Single(m => m.Name == "Any" && m.GetParameters().Length == 2)
.MakeGenericMethod(itemType);
var callToAny = Expression.Call(anyMethod, propBase, matchExpression);
return Expression.Lambda<Func<T, bool>>(callToAny, parameterExpression);
}
Calling it like this:
CreateAnyExpression<Subscription, Invoice>("Client.Invoices", i => i.InvoiceID == 1)
... yields the following Expression<Func<Subscription,bool>>:
s => s.Client.Invoices.Any(i => (i.InvoiceID == 1))
Here's a working program building Linq Expression
{(x.Children.Count(y => y.SomeID == SomeVar) > 0)}
using System;
using System.Linq;
using System.Linq.Expressions;
namespace ExpressionTree
{
class Program
{
static void Main(string[] args)
{
ParameterExpression foundX = Expression.Parameter(typeof(Parent), "x");
Guid[] guids = new Guid[1] { Guid.NewGuid() };
Expression expression = GetCountWithPredicateExpression(guids, foundX);
}
private static Expression GetCountWithPredicateExpression(Guid[] idsToFilter, ParameterExpression foundX)
{
System.Reflection.PropertyInfo childIDPropertyInfo = typeof(Child).GetProperty(nameof(Child.SomeID));
ParameterExpression foundY = Expression.Parameter(typeof(Child), "y");
Expression childIDLeft = Expression.Property(foundY, childIDPropertyInfo);
Expression conditionExpression = Expression.Constant(false, typeof(bool));
foreach (Guid id in idsToFilter)
conditionExpression = Expression.Or(conditionExpression, Expression.Equal(childIDLeft, Expression.Constant(id)));
Expression<Func<Child, bool>> idLambda = Expression.Lambda<Func<Child, bool>>(conditionExpression, foundY);
var countMethod = typeof(Enumerable).GetMethods()
.First(method => method.Name == "Count" && method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(Child));
System.Reflection.PropertyInfo childrenPropertyInfo = typeof(Parent).GetProperty("Children");
Expression childrenLeft = Expression.Property(foundX, childrenPropertyInfo);
Expression ret = Expression.GreaterThan(Expression.Call(countMethod, childrenLeft, idLambda), Expression.Constant(0));
return ret;
}
}
public class Parent
{
public Child[] Children { get; set; }
}
public class Child
{
public int ID { get; set; }
public Guid SomeID { get; set; }
}
}

How do I insert a conversion function into my expression tree?

This is a learning exercise in expression trees.
I have this working code:
class Foo
{
public int A { get; set; }
public string B { get; set; }
}
class Bar
{
public int C { get; set;}
public string D { get; set; }
}
class FieldMap
{
public PropertyInfo From { get; set; }
public PropertyInfo To { get; set; }
}
class Program
{
static Action<TFrom, TTo> CreateMapper<TFrom, TTo>(IEnumerable<FieldMap> fields)
{
ParameterExpression fromParm = Expression.Parameter(typeof(TFrom), "from");
ParameterExpression toParm = Expression.Parameter(typeof(TTo), "to");
//var test = new Func<string, string>(x => x);
//var conversionExpression = Expression.Call(null, test.Method);
var assignments = from fm in fields
let fromProp = Expression.Property(fromParm, fm.From)
let toProp = Expression.Property(toParm, fm.To)
select Expression.Assign(toProp, fromProp);
var lambda = Expression.Lambda<Action<TFrom, TTo>>(
Expression.Block(assignments),
new ParameterExpression[] { fromParm, toParm });
return lambda.Compile();
}
static void Main(string[] args)
{
var pa = typeof(Foo).GetProperty("A");
var pb = typeof(Foo).GetProperty("B");
var pc = typeof(Bar).GetProperty("C");
var pd = typeof(Bar).GetProperty("D");
var mapper = CreateMapper<Foo, Bar>(new FieldMap[]
{
new FieldMap() { From = pa, To = pc },
new FieldMap() { From = pb, To = pd }
});
Foo f = new Foo();
Bar b = new Bar();
f.A = 20;
f.B = "jason";
b.C = 25;
b.D = "matt";
mapper(f, b); // copies properties from f into b
}
}
Works nicely. As noted it copies the corresponding properties from f to b. Now, supposing I wanted to add some conversion or formatting method that takes the "from property", does some magic, and then sets the "to property" equal to the result. Note the two commented out lines in the middle of CreateMapper.
How do I accomplish this? I got this far, but I'm sort of lost now.
Your code sample is almost there; you can use Expression.Call to do the transformation as you are clearly trying to do. Instead of assigning toProp to the fromProp MemberExpression, you can assign to a MethodCallExpression representing the value of the transformation.
The tricky part here is to figure out how to do the transformation, which I assume will vary for different properties.
You can replace the LINQ expression with:
var assignments = from fm in fields
let fromProp = Expression.Property(fromParm, fm.From)
let fromPropType = fm.From.PropertyType
let fromTransformed
= Expression.Call(GetTransform(fromPropType), fromProp)
let toProp = Expression.Property(toParm, fm.To)
select Expression.Assign(toProp, fromTransformed);
(Notice that the right-hand side of the assignment is now fromTransformed rather than fromProp.)
where GetTransform looks something like (I've assumed here that the nature of the transformation depends only on the type of the property):
private static MethodInfo GetTransform(Type type)
{
return typeof(Program).GetMethod(GetTransformName(type));
}
private static string GetTransformName(Type type)
{
if (type == typeof(int))
return "MapInt";
if (type == typeof(string))
return "MapString";
throw new ArgumentException("Unknown type");
}
Then the only thing left to do is filling in the transformations themselves; for example:
public static int MapInt(int x) { return x * 2; }
public static string MapString(string x) { return x + x; }
Then, your usage-test method would produce:
b.c == 40
b.d == "jasonjason"
I had a bit of a play with your code and I think I can give you a nice fluent-style field map builder. Given your classes Foo & Bar you could run this code:
var foo = new Foo() { A = 20, B = "jason", };
var bar = new Bar() { C = 25, D = "matt", };
var fm = new FieldMapBuilder<Foo, Bar>()
.AddMap(f => f.A, b => b.C)
.AddMap(f => f.B, b => b.D)
.AddMap(f => f.A, b => b.D, x => String.Format("!{0}!", x))
.Compile();
fm(foo, bar);
The result is that bar now looks as if it were declared like so:
var bar = new Bar() { C = 20, D = "!20!", };
The nice thing about this code is you don't need to do any reflection in the calling code, property types are inferred, and it neatly handles mapping properties of different types.
Here's the code that does it:
public class FieldMapBuilder<TFrom, TTo>
{
private Expression<Action<TFrom, TTo>>[] _fieldMaps = null;
public FieldMapBuilder()
{
_fieldMaps = new Expression<Action<TFrom, TTo>>[] { };
}
public FieldMapBuilder(Expression<Action<TFrom, TTo>>[] fieldMaps)
{
_fieldMaps = fieldMaps;
}
public FieldMapBuilder<TFrom, TTo> AddMap<P>(
Expression<Func<TFrom, P>> source,
Expression<Func<TTo, P>> destination)
{
return this.AddMap<P, P>(source, destination, x => x);
}
public FieldMapBuilder<TFrom, TTo> AddMap<PFrom, PTo>(
Expression<Func<TFrom, PFrom>> source,
Expression<Func<TTo, PTo>> destination,
Expression<Func<PFrom, PTo>> map)
{
var paramFrom = Expression.Parameter(typeof(TFrom), "from");
var paramTo = Expression.Parameter(typeof(TTo), "to");
var invokeExpressionFrom =
Expression.Invoke(map, Expression.Invoke(source, paramFrom));
var propertyExpressionTo =
Expression.Property(paramTo,
(destination.Body as MemberExpression).Member as PropertyInfo);
var assignmentExpression =
Expression.Assign(propertyExpressionTo, invokeExpressionFrom);
return new FieldMapBuilder<TFrom, TTo>(
_fieldMaps.Concat(new Expression<Action<TFrom, TTo>>[]
{
Expression.Lambda<Action<TFrom, TTo>>(
assignmentExpression,
paramFrom,
paramTo)
}).ToArray());
}
public Action<TFrom, TTo> Compile()
{
var paramFrom = Expression.Parameter(typeof(TFrom), "from");
var paramTo = Expression.Parameter(typeof(TTo), "to");
var expressionBlock =
Expression.Block(_fieldMaps
.Select(fm => Expression.Invoke(fm, paramFrom, paramTo))
.ToArray());
var lambda = Expression.Lambda<Action<TFrom, TTo>>(
expressionBlock,
paramFrom,
paramTo);
return lambda.Compile();
}
}

Categories

Resources