In Signum.Utilities.ExpressionTrees there are some general purpose and utility classes for working with ExpressionTrees and making a LinqProvider.
These classes are used by Signum.Engine Linq provider as well as other parts of the framework. They are public but are meant to be used in very Advanced scenarios (building a new Linq Provider).
If don't want to get mad, skip this page.
Now the Linq provider extensibility model are also sin Signum.Utilities, take a look in the
next page.
Query<T>
Basic implementation of IQueryable<T>. Following Microsoft guidelines, relys in QueryProvider for most of the functionality. You should use the class as-is, without inheriting.
public class Query<T> : IQueryable<T>, IQueryable, IEnumerable<T>, IEnumerable, IOrderedQueryable<T>, IOrderedQueryable
{
QueryProvider provider;
Expression expression;
public Query(QueryProvider provider)
public Query(QueryProvider provider, Expression expression)
public Expression Expression {get;}
public Type ElementType {get;}
public IQueryProvider Provider {get;}
public IEnumerator<T> GetEnumerator() {get;}
IEnumerator IEnumerable.GetEnumerator() {get;}
public override string ToString();
public string QueryText{get;}
}
QueryProvider
Implements all the 'bureaucratic' so you can inherit an focus on the two important methods in order to implement your own Linq provider: Execute and GetQueryText (debugging support only). Your provider should inherit from this class.
Here's the full source code:
public abstract class QueryProvider : IQueryProvider
{
protected QueryProvider()
{
}
IQueryable<S> IQueryProvider.CreateQuery<S>(Expression expression)
{
return new Query<S>(this, expression);
}
IQueryable IQueryProvider.CreateQuery(Expression expression)
{
Type elementType = ReflectionTools.CollectionType(expression.Type) ?? expression.Type;
try
{
return (IQueryable)Activator.CreateInstance(typeof(Query<>).MakeGenericType(elementType), new object[] { this, expression });
}
catch (TargetInvocationException tie)
{
throw tie.InnerException;
}
}
S IQueryProvider.Execute<S>(Expression expression)
{
return (S)this.Execute(expression);
}
object IQueryProvider.Execute(Expression expression)
{
return this.Execute(expression);
}
public abstract string GetQueryText(Expression expression);
public abstract object Execute(Expression expression);
}
ExpressionVisitor
Since Expression Trees are immutable, in order to make a change in one node you need to re-create all the nodes affected by the change (chain of parents). This would be a pain in the neck if you don't have some infrastructure to do this automatically. ExpressionVisitor is just a copy of the
Microsoft Implementation. In order to make any transformation on an expression tree you should inherit from this class and override the necessary methods.
public abstract class ExpressionVisitor
{
protected ExpressionVisitor()
{
}
protected virtual Expression Visit(Expression exp);
protected virtual Expression VisitUnary(UnaryExpression u)
protected virtual Expression VisitBinary(BinaryExpression b)
protected virtual Expression VisitTypeIs(TypeBinaryExpression b)
(and so on...)
}
ExpressionNominator
Expression Visitor is able to detect constant subtrees of an expression tree that could potentially be replaced by just a value. This capability is what allows that IQueryable Linq queries blur the boundaries between your code and, presumably, SQL.
public class ExpressionNominator : ExpressionVisitor
{
public static HashSet<Expression> Nominate(Expression expression)
}
ExpressionEvaluator
Expression Visitor that takes the nominated subtrees of the Expression Nominator, compiles and evaluates them and then replaces each subtree by the corresponding value.
ExpressionEvaluator.Eval also is able to evaluate a complete expression tree and return the value (or throw an exception if it's not possible to do so).
public class ExpressionEvaluator : ExpressionVisitor
{
public static Expression PartialEval(Expression exp);
public static object Eval(Expression expression);
}
ExpressionToString
All the Expression nodes defined by .Net Framework use an internal method BuildString (using StringBuilder) to perform ToString functionality. Since this is a good idea for performance reasons it makes it really hard to compose with user-defined expressions, since your ToString never gets called when part of a .Net Expression node. In order to make it work is necessary to replicate all the ToString logic in a visitor.
internal class ExpressionToString: ExpressionVisitor
{
public static string NiceToString(Expression exp)
}
Then, instead of call ToString over your BinaryExpression, call NiceToString instead.
ExpressionHelper
Static helper class commonly used by Expression Visitors.
public static class ExpressionHelper
{
public static ReadOnlyCollection<T> NewIfChange<T>(this ReadOnlyCollection<T> collection, Func<T,T> newValue)
public static List<T> NewIfChange<T>(this List<T> collection, Func<T, T> newValue);
public static Expression TryConvert(this Expression expression, Type type)
public static Expression Nullify(this Expression expression)
public static Expression GetArgument(this MethodCallExpression mce, string parameterName);
public static Expression TryGetArgument(this MethodCallExpression mce, string parameterName);
public static LambdaExpression StripQuotes(this Expression e);
public static bool IsBase(this IQueryable query);
}
CSharpRenderer
Utility class is useful for C# code-generation scenarios.
public static class CSharpRenderer
{
public static string GenerateCSharpCode(this Expression expression);
public static string GenerateCSharpCode(this Expression expression, string[] importedNamespaces);
public static T Collapse<T>(this T obj);
public static T Literal<T>(string literal);
public static string ParameterName(this ParameterInfo pi);
public static string PropertyName(this PropertyInfo pi);
public static string FieldName(this FieldInfo pi);
public static string MethodName(this MethodInfo method);
public static string MethodSignature(this MethodInfo method);
public static string MemberName(this MemberInfo mi);
public static string TypeName(this Type type);
public static string CleanIdentifiers(this string str)
{
return str
.Replace("<>f__AnonymousType", "α")
.Replace("<>h__TransparentIdentifier", "τ");
}
public static string Value(object valor, Type type, string[] importedNamespaces);
public static CodeExpression GetRightExpressionForValue(object value, Type type, string[] importedNamespaces)
public static CodeTypeReference TypeReference(Type type, string[] importedNamespaces)
}