/// <summary>
 /// Returns a registered column, or null if not found
 /// This method requires the table to be already registered
 /// </summary>
 /// <param name="table"></param>
 /// <param name="name"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 protected virtual ColumnExpression GetRegisteredColumn(TableExpression table, string name,
                                                        BuilderContext builderContext)
 {
     return
         (from queryColumn in builderContext.EnumerateScopeColumns()
          where queryColumn.Table.IsEqualTo(table) && queryColumn.Name == name
          select queryColumn).SingleOrDefault();
 }
 protected virtual Expression Analyze(Expression expression, BuilderContext builderContext)
 {
     foreach (var analyze in GetAnalyzers())
     {
         var e = analyze(expression);
         if (e != null)
             return e;
     }
     return expression;
 }
Example #3
0
 protected virtual Expression AnalyzeExpression(Expression expression, BuilderContext builderContext)
 {
     // string Add --> Concat
     var binaryExpression = expression as BinaryExpression;
     if (expression.NodeType == ExpressionType.Add
         && binaryExpression != null && typeof(string).IsAssignableFrom(binaryExpression.Left.Type))
     {
         return new SpecialExpression(SpecialExpressionType.Concat, binaryExpression.Left, binaryExpression.Right);
     }
     return expression;
 }
        protected Expression Analyze(Expression expression, BuilderContext builderContext)
        {
            // small optimization
            if (expression is ConstantExpression)
                return expression;

            expression = AnalyzeNull(expression, builderContext);
            expression = AnalyzeNot(expression, builderContext);
            // constant optimization at last, because the previous optimizations may generate constant expressions
            expression = AnalyzeConstant(expression, builderContext);
            return expression;
        }
 /// <summary>
 /// Returns an existing table or registers the current one
 /// </summary>
 /// <param name="tableExpression"></param>
 /// <param name="builderContext"></param>
 /// <returns>A registered table or the current newly registered one</returns>
 public virtual TableExpression RegisterTable(TableExpression tableExpression, BuilderContext builderContext)
 {
     // 1. Find the table in current scope
     var foundTableExpression = (from t in builderContext.EnumerateScopeTables()
                                 where t.IsEqualTo(tableExpression)
                                 select t).SingleOrDefault();
     if (foundTableExpression != null)
         return foundTableExpression;
     // 2. Find it in all scopes, and promote it to current scope.
     foundTableExpression = PromoteTable(tableExpression, builderContext);
     if (foundTableExpression != null)
         return foundTableExpression;
     // 3. Add it
     builderContext.CurrentSelect.Tables.Add(tableExpression);
     return tableExpression;
 }
 protected virtual Expression AnalyzeConstant(Expression expression, BuilderContext builderContext)
 {
     // we try to find a non-constant operand, and if we do, we won't change this expression
     foreach (var operand in expression.GetOperands())
     {
         if (!(operand is ConstantExpression))
             return expression;
     }
     // now, we just simply return a constant with new value
     try
     {
         var optimizedExpression = Expression.Constant(expression.Evaluate());
         // sometimes, optimizing an expression changes its type, and we just can't allow this.
         if (optimizedExpression.Type == expression.Type)
             return optimizedExpression;
     }
         // if we fail to evaluate the expression, then just return it
     catch (ArgumentException) { }
     return expression;
 }
 public virtual void UnregisterParameter(InputParameterExpression expression, BuilderContext builderContext)
 {
     builderContext.ExpressionQuery.Parameters.Remove(expression);
 }
 /// <summary>
 /// Registers an external parameter
 /// Since these can be complex expressions, we don't try to identify them
 /// and push them every time
 /// The only loss may be a small memory loss (if anyone can prove me that the same Expression can be used twice)
 /// </summary>
 /// <param name="expression"></param>
 /// <param name="alias"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 public virtual InputParameterExpression RegisterParameter(Expression expression, string alias, BuilderContext builderContext)
 {
     var queryParameterExpression = new InputParameterExpression(expression, alias);
     builderContext.ExpressionQuery.Parameters.Add(queryParameterExpression);
     return queryParameterExpression;
 }
 public virtual Expression Parse(Expression expression, BuilderContext builderContext)
 {
     return expression.Recurse(e => Analyze(e, builderContext));
 }
        /// <summary>
        /// Creates a new BuilderContext where parameters have a local scope
        /// </summary>
        /// <returns></returns>
        public BuilderContext NewQuote()
        {
            var builderContext = new BuilderContext();

            // scope independent parts
            builderContext.QueryContext = QueryContext;
            builderContext.ExpressionQuery = ExpressionQuery;
            builderContext.MetaTables = MetaTables;
            builderContext.currentScopeIndex = currentScopeIndex;
            builderContext.SelectExpressions = SelectExpressions;
            builderContext.ExpectMetaTableDefinition = ExpectMetaTableDefinition;

            // scope dependent parts
            builderContext.Parameters = new Dictionary<string, Expression>(Parameters);

            return builderContext;
        }
 public virtual LambdaExpression BuildTableReader1(Type tableType, IList<string> parameters, BuilderContext builderContext)
 {
     var dataRecordParameter = Expression.Parameter(typeof(IDataRecord), "dataRecord");
     var mappingContextParameter = Expression.Parameter(typeof(MappingContext), "mappingContext");
     var table = builderContext.QueryContext.DataContext.Mapping.GetTable(tableType);
     var bindings = new List<MemberBinding>();
     foreach (var column in DataMapper.GetColumns(table))
     {
         var columnName = DataMapper.GetColumnName(tableType, column, builderContext.QueryContext.DataContext);
         var invoke = GetOutputValueReader(column.GetMemberType(), GetTableIndex(parameters, columnName),
                                           dataRecordParameter, mappingContextParameter);
         var parameterColumn = GetOutputValueReader(invoke, dataRecordParameter, mappingContextParameter,
                                                    builderContext);
         var binding = Expression.Bind(column, parameterColumn);
         bindings.Add(binding);
     }
     var newExpression = Expression.New(tableType);
     var initExpression = Expression.MemberInit(newExpression, bindings);
     return Expression.Lambda(initExpression, dataRecordParameter, mappingContextParameter);
 }
 /// <summary>
 /// Registers the table as returned by the SQL request.
 /// Actually, the table is split into its columns.
 /// </summary>
 /// <param name="tableExpression"></param>
 /// <param name="dataRecordParameter"></param>
 /// <param name="mappingContextParameter"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 protected virtual Expression GetOutputTableReader(TableExpression tableExpression,
                                                   ParameterExpression dataRecordParameter, ParameterExpression mappingContextParameter,
                                                   BuilderContext builderContext)
 {
     var bindings = new List<MemberBinding>();
     foreach (var columnExpression in RegisterAllColumns(tableExpression, builderContext))
     {
         var parameterColumn = GetOutputValueReader(columnExpression,
                                                    dataRecordParameter, mappingContextParameter, builderContext);
         var binding = Expression.Bind(columnExpression.MemberInfo, parameterColumn);
         bindings.Add(binding);
     }
     var newExpression = Expression.New(tableExpression.Type);
     var initExpression = Expression.MemberInit(newExpression, bindings);
     return initExpression;
 }
 /// <summary>
 /// Registers all columns of a table.
 /// </summary>
 /// <param name="tableExpression"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 public virtual IEnumerable<ColumnExpression> RegisterAllColumns(TableExpression tableExpression, BuilderContext builderContext)
 {
     foreach (var metaMember in builderContext.QueryContext.DataContext.Mapping.GetTable(tableExpression.Type).RowType.PersistentDataMembers)
     {
         yield return RegisterColumn(tableExpression, metaMember.Member, builderContext);
     }
 }
 /// <summary>
 /// Promotes a table to a common parent between its current scope and our current scope
 /// </summary>
 /// <param name="tableExpression"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 protected virtual TableExpression PromoteTable(TableExpression tableExpression, BuilderContext builderContext)
 {
     // 1. Find the table ScopeExpression
     SelectExpression oldSelect = FindTableScope(ref tableExpression, builderContext);
     if (oldSelect == null)
         return null;
     // 2. Find a common ScopeExpression
     var commonScope = FindCommonScope(oldSelect, builderContext.CurrentSelect);
     commonScope.Tables.Add(tableExpression);
     return tableExpression;
 }
        /// <summary>
        /// Promotes a table to a common parent between its current scope and our current scope
        /// </summary>
        /// <param name="tableExpression"></param>
        /// <param name="builderContext"></param>
        /// <returns></returns>
        protected virtual TableExpression PromoteTable(TableExpression tableExpression, BuilderContext builderContext)
        {
            int currentIndex = 0;
            SelectExpression oldSelect = null;
            SelectExpression commonScope = null;
            TableExpression foundTable = null;
            do
            {
                // take a select
                oldSelect = builderContext.SelectExpressions[currentIndex];

                // look for a common scope
                if (oldSelect != builderContext.CurrentSelect)
                {
                    commonScope = FindCommonScope(oldSelect, builderContext.CurrentSelect);
                    if (commonScope != null)
                        // if a common scope exists, look for an equivalent table in that select
                        for (int tableIndex = 0; tableIndex < oldSelect.Tables.Count && foundTable == null; tableIndex++)
                        {
                            if (oldSelect.Tables[tableIndex].IsEqualTo(tableExpression))
                            {
                                // found a matching table!
                                foundTable = oldSelect.Tables[tableIndex];
                            }
                        }
                }
                ++currentIndex;
            }
            while (currentIndex < builderContext.SelectExpressions.Count && foundTable == null);

            if (foundTable != null)
            {
                oldSelect.Tables.Remove(foundTable);
                commonScope.Tables.Add(foundTable);
            }
            return foundTable;
        }
 /// <summary>
 /// Registers the ColumnExpression as returned by the SQL request.
 /// </summary>
 /// <param name="expression"></param>
 /// <param name="dataRecordParameter"></param>
 /// <param name="mappingContextParameter"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 protected virtual Expression GetOutputValueReader(ColumnExpression expression,
                                                   ParameterExpression dataRecordParameter, ParameterExpression mappingContextParameter,
                                                   BuilderContext builderContext)
 {
     int valueIndex = RegisterOutputParameter(expression, builderContext);
     Type storageType = expression.StorageInfo != null ? expression.StorageInfo.GetMemberType() : null;
     return GetOutputValueReader(storageType ?? expression.Type, valueIndex, dataRecordParameter, mappingContextParameter);
 }
        /// <summary>
        /// Creates an entity set creator, to be used at run-time
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="dataRecordParameter"></param>
        /// <param name="mappingContextParameter"></param>
        /// <param name="builderContext"></param>
        /// <returns></returns>
        protected virtual Expression GetEntitySetBuilder(EntitySetExpression expression,
                                                          ParameterExpression dataRecordParameter, ParameterExpression mappingContextParameter,
                                                          BuilderContext builderContext)
        {
            var entityType = expression.EntitySetType.GetGenericArguments()[0];
            List<ElementInit> members = new List<ElementInit>();
            var add = expression.EntitySetType.GetMethod("Add", 
                    BindingFlags.NonPublic | BindingFlags.Instance,
                    null,
                    new Type[] { typeof(KeyValuePair<object, MemberInfo>) },
                    null);

            foreach (var info in expression.Columns)
            {
                var column = info.Key;
                var tk = info.Value;
                MemberInfo memberInfo = column.StorageInfo ?? column.MemberInfo;
                PropertyInfo propertyInfo = memberInfo as PropertyInfo;
                if (propertyInfo == null || propertyInfo.CanWrite)
                {
                    var parameterColumn = GetOutputValueReader(column,
                            dataRecordParameter, mappingContextParameter, builderContext);
                    members.Add(Expression.ElementInit(add, 
                            new Expression[]{
                                Expression.New(typeof(KeyValuePair<object, MemberInfo>).GetConstructor(new Type[]{typeof(object), typeof(MemberInfo)}),
                                    Expression.Convert(parameterColumn, typeof(object)), 
                                    Expression.Constant(tk.Member, typeof(MemberInfo)))}));
                }
            }

            return Expression.ListInit(
                    Expression.New(
                        expression.EntitySetType.GetConstructor(
                            BindingFlags.NonPublic | BindingFlags.Instance,
                            null,
                            new[] { typeof(DataContext) },
                            null),
                        Expression.Constant(builderContext.QueryContext.DataContext)),
                    members);
        }
 /// <summary>
 /// Registers the table as returned by the SQL request.
 /// Actually, the table is split into its columns.
 /// </summary>
 /// <param name="tableExpression"></param>
 /// <param name="dataRecordParameter"></param>
 /// <param name="mappingContextParameter"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 protected virtual Expression GetOutputTableReader(TableExpression tableExpression,
                                                   ParameterExpression dataRecordParameter, ParameterExpression mappingContextParameter,
                                                   BuilderContext builderContext)
 {
     var bindings = new List<MemberBinding>();
     
     foreach (ColumnExpression columnExpression in RegisterAllColumns(tableExpression, builderContext))
     {
         MemberInfo memberInfo = columnExpression.StorageInfo ?? columnExpression.MemberInfo;
         PropertyInfo propertyInfo = memberInfo as PropertyInfo;
         if (propertyInfo == null || propertyInfo.CanWrite)
         {
             var parameterColumn = GetOutputValueReader(columnExpression,
                                                        dataRecordParameter, mappingContextParameter, builderContext);
             var binding = Expression.Bind(memberInfo, parameterColumn);
             bindings.Add(binding);
         }
     }
     var newExpression = Expression.New(tableExpression.Type);
     var initExpression = Expression.MemberInit(newExpression, bindings);
     return initExpression;
 }
        /// <summary>
        /// Creates a new BuilderContext with a new query scope
        /// </summary>
        /// <returns></returns>
        public BuilderContext NewSelect()
        {
            var builderContext = new BuilderContext();

            // we basically copy everything
            builderContext.QueryContext = QueryContext;
            builderContext.ExpressionQuery = ExpressionQuery;
            builderContext.MetaTables = MetaTables;
            builderContext.Parameters = Parameters;
            builderContext.SelectExpressions = SelectExpressions;
            builderContext.ExpectMetaTableDefinition = ExpectMetaTableDefinition;

            // except CurrentScope, of course
            builderContext.currentScopeIndex = SelectExpressions.Count;
            SelectExpressions.Add(new SelectExpression(CurrentSelect));

            return builderContext;
        }
 /// <summary>
 /// Registers a MetaTable
 /// </summary>
 /// <param name="metaTableType"></param>
 /// <param name="aliases"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 public virtual MetaTableExpression RegisterMetaTable(Type metaTableType, IDictionary<MemberInfo, TableExpression> aliases,
                                                      BuilderContext builderContext)
 {
     MetaTableExpression metaTableExpression;
     if (!builderContext.MetaTables.TryGetValue(metaTableType, out metaTableExpression))
     {
         metaTableExpression = new MetaTableExpression(aliases);
         builderContext.MetaTables[metaTableType] = metaTableExpression;
     }
     return metaTableExpression;
 }
 /// <summary>
 /// Registers a where clause in the current context scope
 /// </summary>
 /// <param name="whereExpression"></param>
 /// <param name="builderContext"></param>
 public virtual void RegisterWhere(Expression whereExpression, BuilderContext builderContext)
 {
     builderContext.CurrentSelect.Where.Add(whereExpression);
 }
 protected virtual SelectExpression FindTableScope(ref TableExpression tableExpression, BuilderContext builderContext)
 {
     foreach (var scope in builderContext.SelectExpressions)
     {
         for (int tableIndex = 0; tableIndex < scope.Tables.Count; tableIndex++)
         {
             if (scope.Tables[tableIndex].IsEqualTo(tableExpression))
             {
                 tableExpression = scope.Tables[tableIndex];
                 scope.Tables.RemoveAt(tableIndex);
                 return scope;
             }
         }
     }
     return null;
 }
 /// <summary>
 /// Registers an expression to be returned by main request.
 /// The strategy is to try to find it in the already registered parameters, and if not found, add it
 /// </summary>
 /// <param name="expression">The expression to be registered</param>
 /// <param name="builderContext"></param>
 /// <returns>Expression index</returns>
 public virtual int RegisterOutputParameter(Expression expression, BuilderContext builderContext)
 {
     var scope = builderContext.CurrentSelect;
     var operands = scope.Operands.ToList();
     for (int index = 0; index < operands.Count; index++)
     {
         if (ExpressionEquals(operands[index], expression))
             return index;
     }
     operands.Add(expression);
     builderContext.CurrentSelect = (SelectExpression)scope.Mutate(operands);
     return operands.Count - 1;
 }
 /// <summary>
 /// Registers a column
 /// This method requires the table to be already registered
 /// </summary>
 /// <param name="table"></param>
 /// <param name="memberInfo"></param>
 /// <param name="name"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 public ColumnExpression RegisterColumn(TableExpression table,
                                        MemberInfo memberInfo, string name,
                                        BuilderContext builderContext)
 {
     if (memberInfo == null)
         return null;
     var queryColumn = GetRegisteredColumn(table, name, builderContext);
     if (queryColumn == null)
     {
         table = RegisterTable(table, builderContext);
         queryColumn = CreateColumn(table, memberInfo, builderContext);
         builderContext.CurrentSelect.Columns.Add(queryColumn);
     }
     return queryColumn;
 }
 /// <summary>
 /// Builds a Row builder, based on a given list of parameters
 /// </summary>
 /// <param name="tableType"></param>
 /// <param name="parameters"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 public virtual LambdaExpression BuildTableReader(Type tableType, IList<string> parameters, BuilderContext builderContext)
 {
     var dataRecordParameter = Expression.Parameter(typeof(IDataRecord), "dataRecord");
     var mappingContextParameter = Expression.Parameter(typeof(MappingContext), "mappingContext");
     //var table = builderContext.QueryContext.DataContext.Mapping.GetTable(tableType);
     var bindings = new List<MemberBinding>();
     for (int parameterIndex = 0; parameterIndex < parameters.Count; parameterIndex++)
     {
         var parameter = parameters[parameterIndex];
         var memberInfo = tableType.GetSingleMember(parameter);
         if (memberInfo == null)
         {
             memberInfo = tableType.GetSingleMember(parameter, BindingFlags.Public | BindingFlags.NonPublic
                                                               | BindingFlags.Instance | BindingFlags.IgnoreCase);
         }
         // TODO real error
         if (memberInfo == null)
             throw new ArgumentException(string.Format("Invalid column '{0}'", parameter));
         //var column = DataMapper.GetColumnName(tableType, memberInfo, builderContext.QueryContext.DataContext);
         //var columnName = DataMapper.GetColumnName(tableType, memberInfo, builderContext.QueryContext.DataContext);
         var invoke = GetOutputValueReader(memberInfo.GetMemberType(), parameterIndex, //GetTableIndex(parameters, columnName),
                                           dataRecordParameter, mappingContextParameter);
         var parameterColumn = GetOutputValueReader(invoke, dataRecordParameter, mappingContextParameter,
                                                    builderContext);
         var binding = Expression.Bind(memberInfo, parameterColumn);
         bindings.Add(binding);
     }
     var newExpression = Expression.New(tableType);
     var initExpression = Expression.MemberInit(newExpression, bindings);
     return Expression.Lambda(initExpression, dataRecordParameter, mappingContextParameter);
 }
 /// <summary>
 /// Creates a default TableExpression
 /// </summary>
 /// <param name="tableType"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 public virtual TableExpression CreateTable(Type tableType, BuilderContext builderContext)
 {
     return new TableExpression(tableType, DataMapper.GetTableName(tableType, builderContext.QueryContext.DataContext));
 }
 /// <summary>
 /// Registers the expression as returned by the SQL request.
 /// </summary>
 /// <param name="expression"></param>
 /// <param name="dataRecordParameter"></param>
 /// <param name="mappingContextParameter"></param>
 /// <param name="builderContext"></param>
 /// <returns></returns>
 protected virtual Expression GetOutputValueReader(Expression expression,
                                                   ParameterExpression dataRecordParameter, ParameterExpression mappingContextParameter,
                                                   BuilderContext builderContext)
 {
     int valueIndex = RegisterOutputParameter(expression, builderContext);
     return GetOutputValueReader(expression.Type, valueIndex, dataRecordParameter, mappingContextParameter);
 }
        /// <summary>
        /// Registers an association
        /// </summary>
        /// <param name="tableExpression">The table holding the member, to become the joinedTable</param>
        /// <param name="tableMemberInfo"></param>
        /// <param name="builderContext"></param>
        /// <returns></returns>
        public virtual TableExpression RegisterAssociation(TableExpression tableExpression, MemberInfo tableMemberInfo,
                                                           BuilderContext builderContext)
        {
            IList<MemberInfo> theseKeys, otherKeys;
            TableJoinType joinType;
            string joinID;
            var otherTableType = DataMapper.GetAssociation(tableExpression, tableMemberInfo, out theseKeys, out otherKeys, 
                                                      out joinType, out joinID, builderContext.QueryContext.DataContext);
            // if the memberInfo has no corresponding association, we get a null, that we propagate
            if (otherTableType == null)
                return null;

            // the current table has the foreign key, the other table the referenced (usually primary) key
            if (theseKeys.Count != otherKeys.Count)
                throw Error.BadArgument("S0128: Association arguments (FK and ref'd PK) don't match");

            // we first create the table, with the JoinID, and we MUST complete the table later, with the Join() method
            var otherTableExpression = new TableExpression(otherTableType, DataMapper.GetTableName(otherTableType, builderContext.QueryContext.DataContext), joinID);

            Expression joinExpression = null;

            var createdColumns = new List<ColumnExpression>();
            for (int keyIndex = 0; keyIndex < theseKeys.Count; keyIndex++)
            {
                // joinedKey is registered, even if unused by final select (required columns will be filtered anyway)
                Expression otherKey = RegisterColumn(otherTableExpression, otherKeys[keyIndex], builderContext);
                // foreign is created, we will store it later if this assocation is registered too
                Expression thisKey = CreateColumn(tableExpression, theseKeys[keyIndex], builderContext);
                createdColumns.Add((ColumnExpression)thisKey);

                // if the key is nullable, then convert it
                // TODO: this will probably need to be changed
                if (otherKey.Type.IsNullable())
                    otherKey = Expression.Convert(otherKey, otherKey.Type.GetNullableType());
                if (thisKey.Type.IsNullable())
                    thisKey = Expression.Convert(thisKey, thisKey.Type.GetNullableType());
                var referenceExpression = Expression.Equal(thisKey, otherKey);

                // if we already have a join expression, then we have a double condition here, so "AND" it
                if (joinExpression != null)
                    joinExpression = Expression.And(joinExpression, referenceExpression);
                else
                    joinExpression = referenceExpression;
            }
            // we complete the table here, now that we have all join information
            otherTableExpression.Join(joinType, tableExpression, joinExpression);

            // our table is created, with the expressions
            // now check if we didn't register exactly the same
            if ((from t in builderContext.EnumerateScopeTables() where t.IsEqualTo(otherTableExpression) select t).SingleOrDefault() == null)
            {
                builderContext.CurrentSelect.Tables.Add(otherTableExpression);
                foreach (var createdColumn in createdColumns)
                    builderContext.CurrentSelect.Columns.Add(createdColumn);
            }

            return otherTableExpression;
        }
 public ColumnExpression CreateColumn(TableExpression table, MemberInfo memberInfo, BuilderContext builderContext)
 {
     var dataMember = builderContext.QueryContext.DataContext.Mapping.GetTable(table.Type).RowType
         .GetDataMember(memberInfo);
     if (dataMember == null)
         return null;
     return new ColumnExpression(table, dataMember.MappedName, memberInfo);
 }
        public BuilderContext Clone()
        {
            var builderContext = new BuilderContext();

            builderContext.QueryContext = QueryContext;
            builderContext.ExpressionQuery = ExpressionQuery;
            builderContext.MetaTables = MetaTables;
            builderContext.Parameters = Parameters;
            builderContext.SelectExpressions = SelectExpressions;
            builderContext.currentScopeIndex = currentScopeIndex;
            builderContext.ExpectMetaTableDefinition = ExpectMetaTableDefinition;

            return builderContext;
        }