How to add "Or" or "OrElse" in MakeComparison - c#

I have a method for dynamic creating a query. This works fine. But I also want to add "Or" or "OrElse". I have searched the internet but can't find an example. I would be also nice to have also an "in" in the query.
When I try "or" or "OrElse" I see in the logfile that it put an "And" and not an "Or".
I also get this error: The binary operator OrElse is not defined for the types 'System.Nullable1[System.Int16]' and 'System.Nullable1[System.Int16
private static Expression MakeComparison(Expression left, string comparison, string value)
{
switch (comparison)
{
case "==":
case "IsEqualTo":
return MakeBinary(ExpressionType.Equal, left, value);
case "!=":
case "IsNotEqualTo":
return MakeBinary(ExpressionType.NotEqual, left, value);
case ">":
case "IsGreaterThan":
return MakeBinary(ExpressionType.GreaterThan, left, value);
case ">=":
case "IsGreaterThanOrEqualTo":
return MakeBinary(ExpressionType.GreaterThanOrEqual, left, value);
case "<":
case "IsLessThan":
return MakeBinary(ExpressionType.LessThan, left, value);
case "<=":
case "IsLessThanOrEqualTo":
return MakeBinary(ExpressionType.LessThanOrEqual, left, value);
case "OrElse":
return MakeBinary(ExpressionType.OrElse, left, value);
case "Or":
return MakeBinary(ExpressionType.Or, left, value);
case "Contains":
case "StartsWith":
case "EndsWith":
return Expression.Call(MakeString(left), comparison, Type.EmptyTypes, Expression.Constant(value, typeof(string)));
default:
throw new NotSupportedException($"Invalid comparison operator '{comparison}'.");
}
private static Expression MakeBinary(ExpressionType type, Expression left, string value)
{
object typedValue = value;
if (left.Type != typeof(string))
{
if (string.IsNullOrEmpty(value))
{
typedValue = null;
if (Nullable.GetUnderlyingType(left.Type) == null)
left = Expression.Convert(left, typeof(Nullable<>).MakeGenericType(left.Type));
}
else
{
var valueType = Nullable.GetUnderlyingType(left.Type) ?? left.Type;
typedValue = valueType.IsEnum ? Enum.Parse(valueType, value) :
valueType == typeof(Guid) ? Guid.Parse(value) :
Convert.ChangeType(value, valueType);
}
}
var right = Expression.Constant(typedValue, left.Type);
return Expression.MakeBinary(type, left, right);
}
}

Related

Trying to create a morethan, equal or greaterthan dynamic filter for dates in linq

I have been trying to create a expression tree filter for Linq that take 2 dates and a string of possible values { "lessthan", "equals", "morethan" }. I wish to format the call to be something like Query.Where(CompareDates(x => x.left, right, "less than"));
I have the code:
public static IQueryable<TSource> CompareDates<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, DateTime?>> left,
DateTime? right,
string equality)
{
if (right == null || string.IsNullOrWhiteSpace(equality))
return source;
var p = left.Parameters.Single();
Expression member = p;
Expression leftExpression = Expression.Property(member, "left");
Expression rightParameter = Expression.Constant(right, typeof(DateTime));
BinaryExpression BExpression = null;
switch (equality)
{
case "lessthan":
BExpression = Expression.LessThan(leftExpression, rightParameter);
break;
case "equal":
BExpression = Expression.Equal(leftExpression, rightParameter);
break;
case "morethan":
BExpression = Expression.GreaterThan(leftExpression, rightParameter);
break;
default:
throw new Exception(String.Format("Equality {0} not recognised.", equality));
}
return source.Where(Expression.Lambda<Func<TSource, bool>>(BExpression, p));
}
Unfortunately it is producing an error of "System.ArgumentException: Instance property 'left' is not defined for type 'Model' at System.Linq.Expressions.Expression.Property(Expression expression, String propertyName) at SARRestAPI.Extensions.Expressions.CompareDates[TSource](IQueryable1 source, Expression1 src, DateTime supplied, String equality)"
Anyone have an ideas why this is happening?
Here we go; what you want to do is use the .Body of the incoming selector, not look for .left. Meaning, given an input selector of x => x.Foo.Bar.Blap, a constant, and a comparison, you want to construct something like x => x.Foo.Bar.Blap < someValue, by reusing both the x (the parameter, which you're already doing) and the body (x.Foo.Bar.Blap).
In code (note this works for any TValue, not just DateTime):
public enum Comparison
{
Equal,
NotEqual,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual
}
public static IQueryable<TSource> Compare<TSource, TValue>(
this IQueryable<TSource> source,
Expression<Func<TSource, TValue>> selector,
TValue value,
Comparison comparison)
{
Expression left = selector.Body;
Expression right = Expression.Constant(value, typeof(TValue));
BinaryExpression body;
switch (comparison)
{
case Comparison.LessThan:
body = Expression.LessThan(left, right);
break;
case Comparison.LessThanOrEqual:
body = Expression.LessThanOrEqual(left, right);
break;
case Comparison.Equal:
body = Expression.Equal(left, right);
break;
case Comparison.NotEqual:
body = Expression.NotEqual(left, right);
break;
case Comparison.GreaterThan:
body = Expression.GreaterThan(left, right);
break;
case Comparison.GreaterThanOrEqual:
body = Expression.GreaterThanOrEqual(left, right);
break;
default:
throw new ArgumentOutOfRangeException(nameof(comparison));
}
return source.Where(Expression.Lambda<Func<TSource, bool>>(body, selector.Parameters));
}
Example usage (here using LINQ-to-Objects, but it should work for other LINQ backends, too):
var arr = new[] { new { X = 11 }, new { X = 12 }, new { X = 13 }, new { X = 14 } };
var source = arr.AsQueryable();
var filtered = source.Compare(x => x.X, 12, Comparison.GreaterThan);
foreach (var item in filtered)
{
Console.WriteLine(item.X); // 13 and 14
}
Note that with C# vCurrent you can do:
var body = comparison switch
{
Comparison.LessThan => Expression.LessThan(left, right),
Comparison.LessThanOrEqual => Expression.LessThanOrEqual(left, right),
Comparison.Equal => Expression.Equal(left, right),
Comparison.NotEqual => Expression.NotEqual(left, right),
Comparison.GreaterThan => Expression.GreaterThan(left, right),
Comparison.GreaterThanOrEqual => Expression.GreaterThanOrEqual(left, right),
_ => throw new ArgumentOutOfRangeException(nameof(comparison)),
};
return source.Where(Expression.Lambda<Func<TSource, bool>>(body, selector.Parameters));
which you might fine preferable.

PredicateBuilder reusable DateTime predicate setter

I'm using PredicateBuilder to generate dynamic search clauses. In the sample code below, is there a way that I can modify SetDateTimePredicate so it can be used for any DateTime property on SomeType?
Expression<Func<SomeType, bool>> somePredicate = null;
somePredicate = somePredicate.Or(
SetDateTimePredicate(comparisonOperator, dateTime1, dateTime2));
private Expression<Func<SomeType, bool>> SetDateTimePredicate(
Enums.ComparisonOperator comparison,
DateTime dateTime1,
DateTime dateTime2)
{
switch (comparison)
{
case Enums.ComparisonOperator.IsLessThan:
return p => p.SomeDateProperty < dateTime1;
case Enums.ComparisonOperator.IsLessThanOrEqualTo:
return p => p.SomeDateProperty <= dateTime1;
case Enums.ComparisonOperator.IsGreaterThan:
return p => p.SomeDateProperty > dateTime1;
case Enums.ComparisonOperator.IsGreaterThanOrEqualTo:
return p => p.SomeDateProperty >= dateTime1;
case Enums.ComparisonOperator.IsBetween:
return p => p.SomeDateProperty >= dateTime1
&& p.SomeDateProperty <= dateTime2;
default:
return p => p.SomeDateProperty == dateTime1;
}
}
I tried an extension method but get this error:
System.NotSupportedException occurred
Message="Method 'Boolean Compare(System.DateTime, ComparisonOperator, System.DateTime, System.DateTime)' has no supported translation to SQL."
Extension method:
public static bool Compare(
this DateTime dateToCompare,
Enums.ComparisonOperator comparison,
DateTime dateTime1,
DateTime dateTime2)
{
switch (comparison)
{
case Enums.ComparisonOperator.IsLessThan:
return dateToCompare < dateTime1;
case Enums.ComparisonOperator.IsLessThanOrEqualTo:
return dateToCompare <= dateTime1;
case Enums.ComparisonOperator.IsGreaterThan:
return dateToCompare > dateTime1;
case Enums.ComparisonOperator.IsGreaterThanOrEqualTo:
return dateToCompare >= dateTime1;
case Enums.ComparisonOperator.IsBetween:
return dateToCompare >= dateTime1
&& dateToCompare <= dateTime2;
default:
return dateToCompare == dateTime1;
}
}
Sample with extension method:
somePredicate = somePredicate.Or(
p => p.SomeDateProperty.Compare(comparisonOperator, dateTime1, dateTime2));
Below is a generic solution, that could be used for any type that has the comparison operators defined not just DateTime. I made the methods, extension methods on the Enum.ComparisionOperator type. It would be used like:
Expression<Func<SomeType, bool>> somePredicate = comparisonOperator.Compare(
(SomeType p) => p.SomeDateProperty, dateTime1, dateTime2));
The methods defined as:
public static Expression<Func<TSource, bool>> Compare<TSource, TValue>(
this Enums.ComparisonOperator comparison,
Expression<Func<TSource, TValue>> source,
TValue value1, TValue value2)
{
var value1Expr = Expression.Constant(value1);
var value2Expr = Expression.Constant(value2);
var newExpr = comparison.CompareExpr(source.Body, value1Expr, value2Expr);
return Expression.Lambda<Func<TSource, bool>>(newExpr, source.Parameters);
}
public static Expression CompareExpr(
this Enums.ComparisonOperator comparison,
Expression exprLeft,
Expression exprRight1, Expression exprRight2)
{
switch (comparison)
{
case Enums.ComparisonOperator.IsLessThan:
return Expression.LessThan(exprLeft, exprRight1);
case Enums.ComparisonOperator.IsLessThanOrEqualTo:
return Expression.LessThanOrEqual(exprLeft, exprRight1);
case Enums.ComparisonOperator.IsGreaterThan:
return Expression.GreaterThan(exprLeft, exprRight1);
case Enums.ComparisonOperator.IsGreaterThanOrEqualTo:
return Expression.GreaterThanOrEqual(exprLeft, exprRight1);
case Enums.ComparisonOperator.IsBetween:
return Expression.AndAlso(
Expression.GreaterThanOrEqual(exprLeft, exprRight1),
Expression.LessThanOrEqual(exprLeft, exprRight2));
default:
return Expression.Equal(exprLeft, exprRight1);
}
}
Edit: I changed the methods above to be generic.

Compare with string values using IQueryable<T> and Expressions

Sorry about the title but I can't really think of a quick way of saying what I want - could someone please change it to a more appropriate one if you think of one?
I'm trying to turn the following function call into an expression query.
List.Compare("propretyName", ">1000");
public static IQueryable<T> Compare<T>(this IQueryable<T> source, string propertyName, string value) {
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "param");
MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, type.GetProperty(propertyName));
string comparisonType = value.Substring(0, 1);
value = value.Length > 0 ? value.Substring(1) : "0";
decimal tmpvalue;
decimal? result = decimal.TryParse(value, out tmpvalue) ? tmpvalue : (decimal?)null;
ConstantExpression constant = Expression.Constant(result, typeof(decimal?));
BinaryExpression comparisonExpression = Expression.GreaterThan(memberAccess, constant);
switch (comparisonType) {
case ">":
comparisonExpression = Expression.GreaterThan(memberAccess, constant);
break;
case "<":
comparisonExpression = Expression.LessThan(memberAccess, constant);
break;
case "=":
comparisonExpression = Expression.Equal(memberAccess, constant);
break;
}
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(comparisonExpression, parameter);
return source.Where(lambda);
}
The above is the method I wrote to make that call.
The lambda at the bottom appears to be correct: {param => (param.propretyName > 1000)}
However, it's not working and I think it's because the particular proprety I'm working on is a decimal? so it should be {param => (param.propertyName.Value > 1000)}.
Could anybody help me out to use the Value rather than. There's just something that's escaping me here.
I can't use the Where(string) method as I'm using Entity-Framework.
Answer found
public static IQueryable<T> Compare<T>(this IQueryable<T> source, string propertyName, string value) {
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "param");
MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, type.GetProperty(propertyName));
//This is the added methods that results in the proper output
PropertyInfo valProp = typeof(Nullable<decimal>).GetProperty("Value");
memberAccess = Expression.MakeMemberAccess(memberAccess, valProp);
string comparisonType = value.Substring(0, 1);
value = value.Length > 0 ? value.Substring(1) : "0";
decimal tmpvalue;
decimal? result = decimal.TryParse(value, out tmpvalue) ? tmpvalue : (decimal?)null;
ConstantExpression constant = Expression.Constant(tmpvalue);
BinaryExpression comparisonExpression = Expression.GreaterThan(memberAccess, constant);
switch (comparisonType) {
case ">":
comparisonExpression = Expression.GreaterThan(memberAccess, constant);
break;
case "<":
comparisonExpression = Expression.LessThan(memberAccess, constant);
break;
case "=":
comparisonExpression = Expression.Equal(memberAccess, constant);
break;
}
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(comparisonExpression, parameter);
return source.Where(lambda);
}
Why not just use the existing Dynamic LINQ library:
myList.Where("propertyName > 1000");

Dynamically Build Linq Lambda Expression

Okay, my guess is this is answered somewhere already, and I'm just not quite familiar enough with the syntax yet to understand, so bear with me.
The users of my web app need to filter a long list of items in a gridview, accessed via a linqdatasource. I am using the OnSelecting Event to further filter items. I want to filter those items based on selections the users make in DropDownLists.
For example, they select "Title" "Contains" "Fred"
This results in
e.Result = dbContext.Opps.Where(opp => opp.Title.Contains("Fred"));
Or "Description" "Does not Contain" "Alpha"
results in
e.Result = dbContext.Opps.Where(opp => !opp.Description.Contains("Alpha"));
I would like to build up that Expression (System.Linq.Expressions.Expression>) dynamically, instead of having nested switch expressions to generate it, as there are a number of fields I want to check, and I also want to use the StartsWith and EndsWith checks. If I could build the Expression as a string, like so:
string stringExpression = string.Format("opp => opp.{0}.{1}(\"{2}\")",
ddlCustomFilter.SelectedValue,
ddlFilterType.SelectedValue,
txtFilterText.Text);
And then somehow have it be converted into an Expression... is this possible? Or should I just bite the bullet and generate all the switch() statements required to create the various expressions?
You could certainly build the expression dynamically, but I would consider using Dynamic LINQ as an alternative first, though you may not be able to use Contains. In addition, you may want to consider using PredicateBuilder to build complex queries additively.
try this code...
call ToExpression Method()....
public static Expression<Func<T, bool>> ToExpression<T>(string andOrOperator, string propName, string opr, string value, Expression<Func<T, bool>> expr = null)
{
Expression<Func<T, bool>> func = null;
try
{
ParameterExpression paramExpr = Expression.Parameter(typeof(T));
var arrProp = propName.Split('.').ToList();
Expression binExpr = null;
string partName = string.Empty;
arrProp.ForEach(x =>
{
Expression tempExpr = null;
partName = partName.IsNull() ? x : partName + "." + x;
if (partName == propName)
{
var member = NestedExprProp(paramExpr, partName);
var type = member.Type.Name == "Nullable`1" ? Nullable.GetUnderlyingType(member.Type) : member.Type;
tempExpr = ApplyFilter(opr, member, Expression.Convert(ToExprConstant(type, value), member.Type));
}
else
tempExpr = ApplyFilter("!=", NestedExprProp(paramExpr, partName), Expression.Constant(null));
if (binExpr != null)
binExpr = Expression.AndAlso(binExpr, tempExpr);
else
binExpr = tempExpr;
});
Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(binExpr, paramExpr);
if (expr != null)
innerExpr = (andOrOperator.IsNull() || andOrOperator == "And" || andOrOperator == "AND" || andOrOperator == "&&") ? innerExpr.And(expr) : innerExpr.Or(expr);
func = innerExpr;
}
catch { }
return func;
}
private static MemberExpression NestedExprProp(Expression expr, string propName)
{
string[] arrProp = propName.Split('.');
int arrPropCount = arrProp.Length;
return (arrPropCount > 1) ? Expression.Property(NestedExprProp(expr, arrProp.Take(arrPropCount - 1).Aggregate((a, i) => a + "." + i)), arrProp[arrPropCount - 1]) : Expression.Property(expr, propName);
}
private static Expression ToExprConstant(Type prop, string value)
{
if (value.IsNull())
return Expression.Constant(value);
object val = null;
switch (prop.FullName)
{
case "System.Guid":
val = value.ToGuid();
break;
default:
val = Convert.ChangeType(value, Type.GetType(prop.FullName));
break;
}
return Expression.Constant(val);
}
private static Expression ApplyFilter(string opr, Expression left, Expression right)
{
Expression InnerLambda = null;
switch (opr)
{
case "==":
case "=":
InnerLambda = Expression.Equal(left, right);
break;
case "<":
InnerLambda = Expression.LessThan(left, right);
break;
case ">":
InnerLambda = Expression.GreaterThan(left, right);
break;
case ">=":
InnerLambda = Expression.GreaterThanOrEqual(left, right);
break;
case "<=":
InnerLambda = Expression.LessThanOrEqual(left, right);
break;
case "!=":
InnerLambda = Expression.NotEqual(left, right);
break;
case "&&":
InnerLambda = Expression.And(left, right);
break;
case "||":
InnerLambda = Expression.Or(left, right);
break;
case "LIKE":
InnerLambda = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right);
break;
case "NOTLIKE":
InnerLambda = Expression.Not(Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right));
break;
}
return InnerLambda;
}
public static Expression<Func<T, object>> PropExpr<T>(string PropName)
{
ParameterExpression paramExpr = Expression.Parameter(typeof(T));
var tempExpr = Extentions.NestedExprProp(paramExpr, PropName);
return Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Lambda(tempExpr, paramExpr).Body, typeof(object)), paramExpr);
}
public static IQueryOver<T, T> OrderBy<T>(this IQueryOver<T, T> Collection, string sidx, string sord)
{
return sord == "asc" ? Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Asc : Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Desc;
}
public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}

Generated methods for polynomial evaluation

I'm trying to come up with an elegant way to handle some generated polynomials. Here's the situation we'll focus on (exclusively) for this question:
order is a parameter in generating an nth order polynomial, where n:=order + 1.
i is an integer parameter in the range 0..n
The polynomial has zeros at x_j, where j = 1..n and j ≠ i (it should be clear at this point that StackOverflow needs a new feature or it's present and I don't know it)
The polynomial evaluates to 1 at x_i.
Since this particular code example generates x_1 .. x_n, I'll explain how they're found in the code. The points are evenly spaced x_j = j * elementSize / order apart, where n = order + 1.
I generate a Func<double, double> to evaluate this polynomial¹.
private static Func<double, double> GeneratePsi(double elementSize, int order, int i)
{
if (order < 1)
throw new ArgumentOutOfRangeException("order", "order must be greater than 0.");
if (i < 0)
throw new ArgumentOutOfRangeException("i", "i cannot be less than zero.");
if (i > order)
throw new ArgumentException("i", "i cannot be greater than order");
ParameterExpression xp = Expression.Parameter(typeof(double), "x");
// generate the terms of the factored polynomial in form (x_j - x)
List<Expression> factors = new List<Expression>();
for (int j = 0; j <= order; j++)
{
if (j == i)
continue;
double p = j * elementSize / order;
factors.Add(Expression.Subtract(Expression.Constant(p), xp));
}
// evaluate the result at the point x_i to get scaleInv=1.0/scale.
double xi = i * elementSize / order;
double scaleInv = Enumerable.Range(0, order + 1).Aggregate(0.0, (product, j) => product * (j == i ? 1.0 : (j * elementSize / order - xi)));
/* generate an expression to evaluate
* (x_0 - x) * (x_1 - x) .. (x_n - x) / (x_i - x)
* obviously the term (x_i - x) is cancelled in this result, but included here to make the result clear
*/
Expression expr = factors.Skip(1).Aggregate(factors[0], Expression.Multiply);
// multiplying by scale forces the condition f(x_i)=1
expr = Expression.Multiply(Expression.Constant(1.0 / scaleInv), expr);
Expression<Func<double, double>> lambdaMethod = Expression.Lambda<Func<double, double>>(expr, xp);
return lambdaMethod.Compile();
}
The problem: I also need to evaluate ψ′=dψ/dx. To do this, I can rewrite ψ=scale×(x_0 - x)(x_1 - x)×..×(x_n - x)/(x_i - x) in the form ψ=α_n×x^n + α_n×x^(n-1) + .. + α_1×x + α_0. This gives ψ′=n×α_n×x^(n-1) + (n-1)×α_n×x^(n-2) + .. + 1×α_1.
For computational reasons, we can rewrite the final answer without calls to Math.Pow by writing ψ′=x×(x×(x×(..) - β_2) - β_1) - β_0.
To do all this "trickery" (all very basic algebra), I need a clean way to:
Expand a factored Expression containing ConstantExpression and ParameterExpression leaves and basic mathematical operations (end up either BinaryExpression with the NodeType set to the operation) - the result here can include InvocationExpression elements to the MethodInfo for Math.Pow which we'll handle in a special manner throughout.
Then I take the derivative with respect to some specified ParameterExpression. Terms in the result where the right hand side parameter to an invocation of Math.Pow was the constant 2 are replaced by the ConstantExpression(2) multiplied by what was the left hand side (the invocation of Math.Pow(x,1) is removed). Terms in the result that become zero because they were constant with respect to x are removed.
Then factor out the instances of some specific ParameterExpression where they occur as the left hand side parameter to an invocation of Math.Pow. When the right hand side of the invocation becomes a ConstantExpression with the value 1, we replace the invocation with just the ParameterExpression itself.
¹ In the future, I'd like the method to take a ParameterExpression and return an Expression that evaluates based on that parameter. That way I can aggregate generated functions. I'm not there yet.
² In the future, I hope to release a general library for working with LINQ Expressions as symbolic math.
I wrote the basics of several symbolic math features using the ExpressionVisitor type in .NET 4. It's not perfect, but it looks like the foundation of a viable solution.
Symbolic is a public static class exposing methods like Expand, Simplify, and PartialDerivative
ExpandVisitor is an internal helper type that expands expressions
SimplifyVisitor is an internal helper type that simplifies expressions
DerivativeVisitor is an internal helper type that takes the derivative of an expression
ListPrintVisitor is an internal helper type that converts an Expression to a prefix notation with a Lisp syntax
Symbolic
public static class Symbolic
{
public static Expression Expand(Expression expression)
{
return new ExpandVisitor().Visit(expression);
}
public static Expression Simplify(Expression expression)
{
return new SimplifyVisitor().Visit(expression);
}
public static Expression PartialDerivative(Expression expression, ParameterExpression parameter)
{
bool totalDerivative = false;
return new DerivativeVisitor(parameter, totalDerivative).Visit(expression);
}
public static string ToString(Expression expression)
{
ConstantExpression result = (ConstantExpression)new ListPrintVisitor().Visit(expression);
return result.Value.ToString();
}
}
Expanding expressions with ExpandVisitor
internal class ExpandVisitor : ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression node)
{
var left = Visit(node.Left);
var right = Visit(node.Right);
if (node.NodeType == ExpressionType.Multiply)
{
Expression[] leftNodes = GetAddedNodes(left).ToArray();
Expression[] rightNodes = GetAddedNodes(right).ToArray();
var result =
leftNodes
.SelectMany(x => rightNodes.Select(y => Expression.Multiply(x, y)))
.Aggregate((sum, term) => Expression.Add(sum, term));
return result;
}
if (node.Left == left && node.Right == right)
return node;
return Expression.MakeBinary(node.NodeType, left, right, node.IsLiftedToNull, node.Method, node.Conversion);
}
/// <summary>
/// Treats the <paramref name="node"/> as the sum (or difference) of one or more child nodes and returns the
/// the individual addends in the sum.
/// </summary>
private static IEnumerable<Expression> GetAddedNodes(Expression node)
{
BinaryExpression binary = node as BinaryExpression;
if (binary != null)
{
switch (binary.NodeType)
{
case ExpressionType.Add:
foreach (var n in GetAddedNodes(binary.Left))
yield return n;
foreach (var n in GetAddedNodes(binary.Right))
yield return n;
yield break;
case ExpressionType.Subtract:
foreach (var n in GetAddedNodes(binary.Left))
yield return n;
foreach (var n in GetAddedNodes(binary.Right))
yield return Expression.Negate(n);
yield break;
default:
break;
}
}
yield return node;
}
}
Taking a derivative with DerivativeVisitor
internal class DerivativeVisitor : ExpressionVisitor
{
private ParameterExpression _parameter;
private bool _totalDerivative;
public DerivativeVisitor(ParameterExpression parameter, bool totalDerivative)
{
if (_totalDerivative)
throw new NotImplementedException();
_parameter = parameter;
_totalDerivative = totalDerivative;
}
protected override Expression VisitBinary(BinaryExpression node)
{
switch (node.NodeType)
{
case ExpressionType.Add:
case ExpressionType.Subtract:
return Expression.MakeBinary(node.NodeType, Visit(node.Left), Visit(node.Right));
case ExpressionType.Multiply:
return Expression.Add(Expression.Multiply(node.Left, Visit(node.Right)), Expression.Multiply(Visit(node.Left), node.Right));
case ExpressionType.Divide:
return Expression.Divide(Expression.Subtract(Expression.Multiply(Visit(node.Left), node.Right), Expression.Multiply(node.Left, Visit(node.Right))), Expression.Power(node.Right, Expression.Constant(2)));
case ExpressionType.Power:
if (node.Right is ConstantExpression)
{
return Expression.Multiply(node.Right, Expression.Multiply(Visit(node.Left), Expression.Subtract(node.Right, Expression.Constant(1))));
}
else if (node.Left is ConstantExpression)
{
return Expression.Multiply(node, MathExpressions.Log(node.Left));
}
else
{
return Expression.Multiply(node, Expression.Add(
Expression.Multiply(Visit(node.Left), Expression.Divide(node.Right, node.Left)),
Expression.Multiply(Visit(node.Right), MathExpressions.Log(node.Left))
));
}
default:
throw new NotImplementedException();
}
}
protected override Expression VisitConstant(ConstantExpression node)
{
return MathExpressions.Zero;
}
protected override Expression VisitInvocation(InvocationExpression node)
{
MemberExpression memberExpression = node.Expression as MemberExpression;
if (memberExpression != null)
{
var member = memberExpression.Member;
if (member.DeclaringType != typeof(Math))
throw new NotImplementedException();
switch (member.Name)
{
case "Log":
return Expression.Divide(Visit(node.Expression), node.Expression);
case "Log10":
return Expression.Divide(Visit(node.Expression), Expression.Multiply(Expression.Constant(Math.Log(10)), node.Expression));
case "Exp":
case "Sin":
case "Cos":
default:
throw new NotImplementedException();
}
}
throw new NotImplementedException();
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == _parameter)
return MathExpressions.One;
return MathExpressions.Zero;
}
}
Simplifying expressions with SimplifyVisitor
internal class SimplifyVisitor : ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression node)
{
var left = Visit(node.Left);
var right = Visit(node.Right);
ConstantExpression leftConstant = left as ConstantExpression;
ConstantExpression rightConstant = right as ConstantExpression;
if (leftConstant != null && rightConstant != null
&& (leftConstant.Value is double) && (rightConstant.Value is double))
{
double leftValue = (double)leftConstant.Value;
double rightValue = (double)rightConstant.Value;
switch (node.NodeType)
{
case ExpressionType.Add:
return Expression.Constant(leftValue + rightValue);
case ExpressionType.Subtract:
return Expression.Constant(leftValue - rightValue);
case ExpressionType.Multiply:
return Expression.Constant(leftValue * rightValue);
case ExpressionType.Divide:
return Expression.Constant(leftValue / rightValue);
default:
throw new NotImplementedException();
}
}
switch (node.NodeType)
{
case ExpressionType.Add:
if (IsZero(left))
return right;
if (IsZero(right))
return left;
break;
case ExpressionType.Subtract:
if (IsZero(left))
return Expression.Negate(right);
if (IsZero(right))
return left;
break;
case ExpressionType.Multiply:
if (IsZero(left) || IsZero(right))
return MathExpressions.Zero;
if (IsOne(left))
return right;
if (IsOne(right))
return left;
break;
case ExpressionType.Divide:
if (IsZero(right))
throw new DivideByZeroException();
if (IsZero(left))
return MathExpressions.Zero;
if (IsOne(right))
return left;
break;
default:
throw new NotImplementedException();
}
return Expression.MakeBinary(node.NodeType, left, right);
}
protected override Expression VisitUnary(UnaryExpression node)
{
var operand = Visit(node.Operand);
ConstantExpression operandConstant = operand as ConstantExpression;
if (operandConstant != null && (operandConstant.Value is double))
{
double operandValue = (double)operandConstant.Value;
switch (node.NodeType)
{
case ExpressionType.Negate:
if (operandValue == 0.0)
return MathExpressions.Zero;
return Expression.Constant(-operandValue);
default:
throw new NotImplementedException();
}
}
switch (node.NodeType)
{
case ExpressionType.Negate:
if (operand.NodeType == ExpressionType.Negate)
{
return ((UnaryExpression)operand).Operand;
}
break;
default:
throw new NotImplementedException();
}
return Expression.MakeUnary(node.NodeType, operand, node.Type);
}
private static bool IsZero(Expression expression)
{
ConstantExpression constant = expression as ConstantExpression;
if (constant != null)
{
if (constant.Value.Equals(0.0))
return true;
}
return false;
}
private static bool IsOne(Expression expression)
{
ConstantExpression constant = expression as ConstantExpression;
if (constant != null)
{
if (constant.Value.Equals(1.0))
return true;
}
return false;
}
}
Formatting expressions for display with ListPrintVisitor
internal class ListPrintVisitor : ExpressionVisitor
{
protected override Expression VisitBinary(BinaryExpression node)
{
string op = null;
switch (node.NodeType)
{
case ExpressionType.Add:
op = "+";
break;
case ExpressionType.Subtract:
op = "-";
break;
case ExpressionType.Multiply:
op = "*";
break;
case ExpressionType.Divide:
op = "/";
break;
default:
throw new NotImplementedException();
}
var left = Visit(node.Left);
var right = Visit(node.Right);
string result = string.Format("({0} {1} {2})", op, ((ConstantExpression)left).Value, ((ConstantExpression)right).Value);
return Expression.Constant(result);
}
protected override Expression VisitConstant(ConstantExpression node)
{
if (node.Value is string)
return node;
return Expression.Constant(node.Value.ToString());
}
protected override Expression VisitParameter(ParameterExpression node)
{
return Expression.Constant(node.Name);
}
}
Testing the results
[TestMethod]
public void BasicSymbolicTest()
{
ParameterExpression x = Expression.Parameter(typeof(double), "x");
Expression linear = Expression.Add(Expression.Constant(3.0), x);
Assert.AreEqual("(+ 3 x)", Symbolic.ToString(linear));
Expression quadratic = Expression.Multiply(linear, Expression.Add(Expression.Constant(2.0), x));
Assert.AreEqual("(* (+ 3 x) (+ 2 x))", Symbolic.ToString(quadratic));
Expression expanded = Symbolic.Expand(quadratic);
Assert.AreEqual("(+ (+ (+ (* 3 2) (* 3 x)) (* x 2)) (* x x))", Symbolic.ToString(expanded));
Assert.AreEqual("(+ (+ (+ 6 (* 3 x)) (* x 2)) (* x x))", Symbolic.ToString(Symbolic.Simplify(expanded)));
Expression derivative = Symbolic.PartialDerivative(expanded, x);
Assert.AreEqual("(+ (+ (+ (+ (* 3 0) (* 0 2)) (+ (* 3 1) (* 0 x))) (+ (* x 0) (* 1 2))) (+ (* x 1) (* 1 x)))", Symbolic.ToString(derivative));
Expression simplified = Symbolic.Simplify(derivative);
Assert.AreEqual("(+ 5 (+ x x))", Symbolic.ToString(simplified));
}

Categories