Signum Framework Logo
"Open framework that encourages convention over configuration, using C# code,
not XML files, to model at the right level of abstraction and achieve deadlines.
...but also has a full Linq provider, and syncs the schema for you!"
Login
RSS

Search

»



Main
Index
Map
Videos
Download
Source code
Tutorials
Forum
FAQ



Image



PoweredBy

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); //Main 'dispatcher' method

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
{
    //Creates a new ReadOnlyCollection<T> if newValue returns a different value for some of them, otherwise the original
    public static ReadOnlyCollection<T> NewIfChange<T>(this ReadOnlyCollection<T> collection, Func<T,T> newValue)
    
    //Creates a new List<T> if newValue returns a different value for some of them, otherwise the original (hack)
    public static List<T> NewIfChange<T>(this List<T> collection, Func<T, T> newValue);

//Tries to convert to type, otherwise returns the original public static Expression TryConvert(this Expression expression, Type type) //Converts and expression of typo T to T? public static Expression Nullify(this Expression expression) //Get the expression associated with the parameter with name parameterName, otherwise throws IndexOutOfRangeException public static Expression GetArgument(this MethodCallExpression mce, string parameterName); //Get the expression associated with the parameter with name parameterName, otherwise returns null public static Expression TryGetArgument(this MethodCallExpression mce, string parameterName);

//Removes all the Quote nodes (signs that a lambda should be a considered an expression) public static LambdaExpression StripQuotes(this Expression e);

//Returns true if a ¬IQueryable Expression is pointing to itself (considered Table) 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); //prints a C#-like string given an expression tree
    public static string GenerateCSharpCode(this Expression expression, string[] importedNamespaces);

public static T Collapse<T>(this T obj); //Dummy method to collapse a collection or object initializer in one single line when using GenerateCSharpCode public static T Literal<T>(string literal); //Dummy method to allow introduce random strings in an expression tree when using GenerateCSharpCode

//Methods that produce a more C#-ish representation of Members than the current ToString method. 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);

//Simplifies debugging of anonymous types and transparen identifiers (i.e.: let ) public static string CleanIdentifiers(this string str) { return str .Replace("<>f__AnonymousType", "α") .Replace("<>h__TransparentIdentifier", "τ"); }

//C#-ish representation of a simple object value (string litera, number, array, simple objects...) public static string Value(object valor, Type type, string[] importedNamespaces); public static CodeExpression GetRightExpressionForValue(object value, Type type, string[] importedNamespaces) //For CodeDom scenarios

//Smart CodeTypeReference constructor that takes importedNamespaces into account public static CodeTypeReference TypeReference(Type type, string[] importedNamespaces) }





Creative Commons License Signum Framework Site by Signum Software is licensed under a Creative Commons Attribution 3.0 License.
Powered by ScrewTurn Wiki version 3.0.5.600.