Example #1
0
 public virtual void BuildSelect(Expression selectExpression, TranslationContext context, Type expectedResultType = null)
 {
     // collect columns, split Expression in
     // - things we will do in CLR
     // - things we will do in SQL
     expectedResultType = expectedResultType ?? selectExpression.Type;
     LambdaExpression recordReaderExpr;
     var dataRecordParameter = Expression.Parameter(typeof(IDataRecord), "dataRecord");
     var sessionParameter = Expression.Parameter(typeof(EntitySession), "session");
     // if we have a GroupByExpression, the result type is not the same:
     // - we need to read what is going to be the Key expression
     // - the final row generator builds a IGrouping<K,T> instead of T
     var selectGroupExpression = selectExpression as GroupExpression;
     if (selectGroupExpression != null) {
       var sqlOutExpr = CutOutSqlTierLambda(selectGroupExpression.GroupedExpression, dataRecordParameter, sessionParameter, null, context);
       var selectKeyExpr = CutOutSqlTierLambda(selectGroupExpression.KeyExpression, dataRecordParameter, sessionParameter, null, context);
       recordReaderExpr = sqlOutExpr;
       if (selectGroupExpression.UseClrGrouping) {
         recordReaderExpr = BuildGroupByPairsReader(sqlOutExpr, selectKeyExpr, dataRecordParameter, sessionParameter, context);
         var grouper = QueryResultsProcessor.CreateGroupBy(selectKeyExpr.Body.Type, sqlOutExpr.Body.Type);
         context.CurrentSelect.ResultsProcessor = grouper;
         context.CurrentSelect.Group.Remove(selectGroupExpression);
       }
     }
     else
       recordReaderExpr = CutOutSqlTierLambda(selectExpression, dataRecordParameter, sessionParameter, expectedResultType, context);
     context.CurrentSelect.Reader = recordReaderExpr;
 }
 /// <summary>
 /// Registers the first table. Extracts the table type and registeres the piece
 /// </summary>
 /// <param name="fromExpression"></param>
 /// <param name="context"></param>
 /// <returns></returns>
 public virtual Expression ExtractFirstTable(Expression fromExpression, TranslationContext context)
 {
     switch(fromExpression.NodeType) {
     case ExpressionType.Call:
       var callExpression = (MethodCallExpression)fromExpression;
       var srcQueryExpr = callExpression.Arguments.Count > 0 ? callExpression.Arguments[0] : null;
       Util.Check(srcQueryExpr != null, "LINQ: failed to create source table for {0}. Expected a query expression as argument. ", fromExpression);
       switch(srcQueryExpr.NodeType) {
     case ExpressionType.Constant:
       // most common case - it is an EntitySet wrapped in constant
       return ExtractFirstTable(srcQueryExpr, context);
     default:
       // special case - source is expression like 'pub.Books' where pub is parameter inside lambda
       return Analyze(srcQueryExpr, context);
       }
     case ExpressionType.Constant:
       var constExpr = (ConstantExpression)fromExpression;
       var entQuery = constExpr.Value as Vita.Entities.Linq.EntityQuery;
       if (entQuery == null)
     break;
       var iLock = entQuery as ILockTarget;
       var lockOptions = iLock == null ? LockOptions.None : iLock.LockOptions;
       return CreateTable(entQuery.ElementType, context, lockOptions);
       }
       Util.Throw("LINQ engine error (ExtractFirstTable): failed to translate expression: {0}", fromExpression);
       return null; //never happens
 }
 protected virtual void AddOffset(Expression offset, TranslationContext context)
 {
     var previousOffset = context.CurrentSelect.Offset;
     if (previousOffset != null)
         context.CurrentSelect.Offset = Expression.Add(offset, previousOffset);
     else
         context.CurrentSelect.Offset = offset;
 }
 protected virtual void AddLimit(Expression limit, TranslationContext context)
 {
     var previousLimit = context.CurrentSelect.Limit;
     if (previousLimit != null)
         context.CurrentSelect.Limit = MergeLimits(previousLimit, limit);
     else
         context.CurrentSelect.Limit = limit;
 }
Example #5
0
 public TranslationContext(TranslationContext source)
 {
     this.DbModel = source.DbModel;
       this.Command = source.Command;
       this.CallStack = source.CallStack;
       this.ExternalValues = source.ExternalValues;
       this.MetaTables = source.MetaTables;
       this.SelectExpressions = source.SelectExpressions;
       this.LambdaParameters = source.LambdaParameters;
       this._currentScopeIndex = source._currentScopeIndex;
 }
        public Expression Analyze(ExpressionChain expressionChain, Expression parameter, TranslationContext context)
        {
            Expression resultExpression = parameter;

            Expression last = expressionChain.Last();
            foreach (Expression expr in expressionChain)
            {
                if (expr == last)
                    context.IsExternalInExpressionChain = true;
                resultExpression = this.Analyze(expr, resultExpression, context);
            }
            return resultExpression;
        }
Example #7
0
        protected Expression Analyze(Expression expression, TranslationContext rewriterContext)
        {
            // small optimization
            if (expression is ConstantExpression)
                return expression;

            // RI: handling comparison with NULL is moved to ExpressionDispatcher
            //expression = AnalyzeNull(expression, rewriterContext);
            expression = AnalyzeNot(expression, rewriterContext);
            expression = AnalyzeBinaryBoolean(expression, rewriterContext);
            // constant optimization at last, because the previous optimizations may generate constant expressions
            //RI: disabled this
            //expression = AnalyzeConstant(expression, rewriterContext);
            return expression;
        }
        //RI: new stuff to handle 2 special cases: 1. Entity comparison 2. Comparison with NULL
        // Comparison with null used to be in ExpressionOptimizer, moved here - it is not optimization, it is essential for correct SQL
        protected virtual Expression CheckEqualOperator(BinaryExpression expression, TranslationContext context)
        {
            var left = expression.Left;
              var right = expression.Right;
              var hasNonNullable = IsNonNullableMember(left) || IsNonNullableMember(right);
              // Try getting PK expressions - not null if we have entity references; replace expression with comparison of PKs
              var leftKey = GetPrimaryKeyAccessor(left, context);
              var rightKey = GetPrimaryKeyAccessor(right, context);
              if(leftKey != null && rightKey != null) {
            var hasParameters = left.NodeType == ExpressionType.Parameter || right.NodeType == ExpressionType.Parameter;
            if(hasParameters && !hasNonNullable && expression.NodeType == ExpressionType.Equal)
              return CreateSqlFunction(SqlFunctionType.EqualNullables, leftKey, rightKey);
            else
              return Expression.MakeBinary(expression.NodeType, leftKey, rightKey);
              }
              // check if it is null comparison
              var isNullFuncType = expression.NodeType == ExpressionType.Equal ? SqlFunctionType.IsNull : SqlFunctionType.IsNotNull;
              if (leftKey != null && right.IsConstNull())
            return CreateSqlFunction(isNullFuncType, leftKey);
              if (rightKey != null && left.IsConstNull())
            return CreateSqlFunction(isNullFuncType, rightKey);
              // null-checks of plain values (strings)
              if (left.IsConstNull())
            return CreateSqlFunction(isNullFuncType, right);
              if (right.IsConstNull())
            return CreateSqlFunction(isNullFuncType, left);

              // If it is Equal, and both are nullable, add matching NULLs;
              //   a == b   -> (a = b) | (a is Null & b IS NULL)
              var forceIgnoreCase = context.Command.Info.Options.IsSet(QueryOptions.ForceIgnoreCase);
              if (forceIgnoreCase && left.Type == typeof(string) & right.Type == typeof(string)) {
            return CreateSqlFunction(SqlFunctionType.StringEqual, forceIgnoreCase, left, right);
              }
              if (expression.NodeType == ExpressionType.Equal && !hasNonNullable &&
            left.NodeType != ExpressionType.Constant && right.NodeType != ExpressionType.Constant
            && left.Type.IsNullable() && right.Type.IsNullable()) {
            return CreateSqlFunction(SqlFunctionType.EqualNullables, forceIgnoreCase, left, right);
              }

              return expression;
        }
Example #9
0
 protected virtual Expression AnalyzeNot(Expression expression, TranslationContext rewriterContext)
 {
     if (expression.NodeType == ExpressionType.Not)
     {
         var notExpression = expression as UnaryExpression;
         var subExpression = notExpression.Operand;
         var subOperands = subExpression.GetOperands().ToArray();
         switch (subExpression.NodeType)
         {
             case ExpressionType.Equal:
                 return Expression.NotEqual(subOperands[0], subOperands[1]);
             case ExpressionType.GreaterThan:
                 return Expression.LessThanOrEqual(subOperands[0], subOperands[1]);
             case ExpressionType.GreaterThanOrEqual:
                 return Expression.LessThan(subOperands[0], subOperands[1]);
             case ExpressionType.LessThan:
                 return Expression.GreaterThanOrEqual(subOperands[0], subOperands[1]);
             case ExpressionType.LessThanOrEqual:
                 return Expression.GreaterThan(subOperands[0], subOperands[1]);
             case ExpressionType.Not:
                 return subOperands[0]; // not not x -> x :)
             case ExpressionType.NotEqual:
                 return Expression.Equal(subOperands[0], subOperands[1]);
             case ExpressionType.Extension:
               if (subExpression is SqlFunctionExpression) {
                 var funcExpr = (SqlFunctionExpression) subExpression;
                 switch(funcExpr.FunctionType) {
                   case SqlFunctionType.IsNotNull:
                     return new SqlFunctionExpression(SqlFunctionType.IsNull, typeof(bool), subOperands);
                   case SqlFunctionType.IsNull:
                     return new SqlFunctionExpression(SqlFunctionType.IsNotNull, typeof(bool), subOperands);
                 }
               }//if
               break;
         }
     }
     return expression;
 }
 /// <summary>
 /// Registers a where clause in the current context scope
 /// </summary>
 /// <param name="whereExpression"></param>
 /// <param name="context"></param>
 public virtual void RegisterWhere(Expression whereExpression, TranslationContext context)
 {
     var where = CheckBoolBitExpression(whereExpression);
     context.CurrentSelect.Where.Add(where);
 }
 public ColumnExpression CreateColumn(TableExpression table, string memberName, TranslationContext context)
 {
     var col = table.TableInfo.GetColumnByMemberName(memberName);
     Util.Check(col != null, "Column for member [{0}] not found in table {1}. Member type is not supported by LINQ.", memberName, table.Name);
     return new ColumnExpression(table, col);
 }
        public ColumnExpression CreateColumn(TableExpression table, string memberName, TranslationContext context)
        {
            var col = table.TableInfo.GetColumnByMemberName(memberName);

            Util.Check(col != null, "Column for member [{0}] not found in table {1}. Member type is not supported by LINQ.", memberName, table.Name);
            return(new ColumnExpression(table, col));
        }
 private Expression GetPrimaryKeyAccessor(Expression expression, TranslationContext context)
 {
     if(!expression.Type.IsInterface)
     return null;
       if (expression.Type.IsDbPrimitive())
     return null;
       if(expression.IsConstNull())
     return null;
       var table = context.DbModel.GetTable(expression.Type, throwIfNotFound: false);
       if (table == null)
     return null;
       var pkMembers = table.PrimaryKey.EntityKey.KeyMembers;
       if (pkMembers.Count > 1)
     Util.Throw("Equal operator for entities with composite key is not supported. Expression: {0}.", expression);
       var pkProp = pkMembers[0].Member.ClrMemberInfo;
       if (pkProp == null) //theoretically it might happen
     Util.Throw("Using hidden members in LINQ is not supported. Expression: {0}.", expression);
       Expression pkRead = Expression.MakeMemberAccess(expression, pkProp);
       // Bug fix - one-to-one relation
       //   in case it is an interface/entity - repeat getting PK again
       if (pkRead.Type.IsInterface)
     pkRead = GetPrimaryKeyAccessor(pkRead, context);
       return pkRead;
 }
 //Checks operands of Join expression: if it returns Entity, replaces it with PK of entity
 private Expression CheckJoinKeySelector(Expression selector, TranslationContext context)
 {
     var tmp = selector;
       if (tmp.NodeType == ExpressionType.Quote)
     tmp = ((UnaryExpression)tmp).Operand;
       // It should be lambda
       var lambda = (LambdaExpression)tmp;
       var retType = lambda.Body.Type;
       if (retType.IsDbPrimitive())
     return selector;
       var newBody = GetPrimaryKeyAccessor(lambda.Body, context);
       if (newBody == null)
     return selector;
       //Build new quoted lambda
       var newSelector = Expression.Quote(Expression.Lambda(newBody, lambda.Parameters));
       return newSelector;
 }
        /// <summary>
        /// Registers all columns of a table.
        /// </summary>
        /// <param name="tableExpression"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public virtual IList <ColumnExpression> RegisterAllColumns(TableExpression tableExpression, TranslationContext context)
        {
            var result = new List <ColumnExpression>();

            foreach (var colInfo in tableExpression.TableInfo.Columns)
            {
                if (colInfo.Member == null)
                {
                    continue;
                }
                var colExpr = RegisterColumn(tableExpression, colInfo.Member.MemberName, context);
                result.Add(colExpr);
            }
            return(result);
        }
 public virtual TableExpression CreateTable(Type tableType, TranslationContext context, LockOptions lockOptions = LockOptions.None)
 {
     var tableInfo = _dbModel.GetTable(tableType);
       var tableExpr = new TableExpression(tableType, tableInfo.FullName, tableInfo, lockOptions);
       return tableExpr;
 }
 /// <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="context"></param>
 /// <returns></returns>
 protected virtual ColumnExpression GetRegisteredColumn(TableExpression table, string name,
     TranslationContext context)
 {
     return
     (from queryColumn in context.EnumerateScopeColumns()
       where queryColumn.Table.IsEqualTo(table) && queryColumn.Name == name
       // where queryColumn.Table == table && queryColumn.Name == name // - RI: this does not work
       select queryColumn).SingleOrDefault();
 }
 // RI: new stuff, optimized entity reader
 protected virtual Expression GetOutputTableReader(TableExpression tableExpression,
     ParameterExpression dataRecordParameter, ParameterExpression sessionParameter,
     TranslationContext context)
 {
     // Note: we have to create materializer each time, because column indexes in output can change from query to query
       var entMatzer = new EntityMaterializer(tableExpression.TableInfo);
       var allColExprs = RegisterAllColumns(tableExpression, context);
       foreach (var col in allColExprs) {
     var colIndex = RegisterOutputValue(col, context);
     entMatzer.AddColumn(col.ColumnInfo, colIndex);
       }
       var entMatzerConst = Expression.Constant(entMatzer);
       var callReadEntity = Expression.Call(entMatzerConst, EntityMaterializer.ReadMethodInfo, dataRecordParameter, sessionParameter);
       var convExpr = Expression.Convert(callReadEntity, tableExpression.Type);
       return convExpr;
 }
        public virtual TableExpression RegisterAssociation(TableExpression tableExpression, EntityMemberInfo refMember, TranslationContext context)
        {
            IList <MemberInfo> otherKeys;
            TableJoinType      joinType;
            string             joinID;
            var theseKeys = GetAssociationMembers(tableExpression, refMember, out otherKeys, out joinType, out joinID);

            // if the memberInfo has no corresponding association, we get a null, that we propagate
            if (theseKeys == null)
            {
                return(null);
            }

            // the current table has the foreign key, the other table the referenced (usually primary) key
            if (theseKeys.Count != otherKeys.Count)
            {
                Util.Throw("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 otherType            = refMember.DataType;
            var otherTableInfo       = context.DbModel.GetTable(otherType);
            var otherTableExpression = CreateTable(otherType, context); // new TableExpression(otherType, otherTableInfo.FullName, otherTableInfo, joinID);

            otherTableExpression = RegisterTable(otherTableExpression, context);
            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], context);
                // foreign is created, we will store it later if this assocation is registered too
                Expression thisKey = CreateColumn(tableExpression, theseKeys[keyIndex].Name, context);
                createdColumns.Add((ColumnExpression)thisKey);

                // the other key is set as left operand, this must be this way
                // since some vendors (SQL Server) don't support the opposite
                var referenceExpression = MakeEqual(otherKey, thisKey);

                // 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
            var existingTable = (from t in context.EnumerateScopeTables() where t.IsEqualTo(otherTableExpression) select t).SingleOrDefault();

            if (existingTable != null)
            {
                return(existingTable);
            }

            context.CurrentSelect.Tables.Add(otherTableExpression);
            foreach (var createdColumn in createdColumns)
            {
                context.CurrentSelect.Columns.Add(createdColumn);
            }
            return(otherTableExpression);
        }
        protected virtual Expression GetOutputValueReader(Expression expression, Type expectedType, ParameterExpression dataRecordParameter,
                                                          ParameterExpression sessionParameter, TranslationContext context)
        {
            expectedType = expectedType ?? expression.Type;
            int valueIndex             = RegisterOutputValue(expression, context);
            Func <object, object> conv = x => x;

            if (expression is ColumnExpression)
            {
                //With column everything is simple
                var colExpr = (ColumnExpression)expression;
                conv = colExpr.ColumnInfo.TypeInfo.ColumnToPropertyConverter;
            }
            else
            {
                //Otherwise get converter from type registry;
                // Why we need converters for non-column values.
                // Example: Count(*) function. In this case expectedType is always 'int', but some providers (MySql) return Int64.
                // In this case expression.Type is Int64 (it is SqlFunction expression), while expectedType is int32. We need a converter.
                // Note - even if expression.Type is the same as expectedType, we still might need a converter.
                // Example : 'Max(decimalCol)' in SQLite. SQLite stores decimals as doubles (we do), so SQL will return double;
                // we need an extra converter to cast to Decimal expected by c# code. We must go through TypeRegistry anyway,
                // to verify how type is supported.
                conv = _dbModel.Driver.TypeRegistry.GetLinqValueConverter(expression.Type, expectedType);
            }
            var reader = ColumnReaderHelper.GetColumnValueReader(dataRecordParameter, valueIndex, expectedType, conv);

            return(reader);
        }
        /// <summary>
        /// Promotes a table to a common parent between its current scope and our current scope
        /// </summary>
        /// <param name="tableExpression"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        protected virtual TableExpression PromoteTable(TableExpression tableExpression, TranslationContext context)
        {
            int currentIndex             = 0;
            SelectExpression oldSelect   = null;
            SelectExpression commonScope = null;
            TableExpression  foundTable  = null;

            do
            {
                // take a select
                oldSelect = context.SelectExpressions[currentIndex];

                // look for a common scope
                if (oldSelect != context.CurrentSelect)
                {
                    commonScope = FindCommonScope(oldSelect, context.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 < context.SelectExpressions.Count && foundTable == null);

            if (foundTable != null)
            {
                oldSelect.Tables.Remove(foundTable);
                commonScope.Tables.Add(foundTable);
            }
            return(foundTable);
        }
        /// <summary>
        /// Returns an existing table or registers the current one
        /// </summary>
        /// <param name="tableExpression"></param>
        /// <param name="context"></param>
        /// <returns>A registered table or the current newly registered one</returns>
        public virtual TableExpression RegisterTable(TableExpression tableExpression, TranslationContext context)
        {
            // 1. Find the table in current scope
            var foundTableExpression = (from t in context.EnumerateScopeTables()
                                        where t.IsEqualTo(tableExpression)
                                        select t).FirstOrDefault();

            if (foundTableExpression != null)
            {
                return(foundTableExpression);
            }
            // 2. Find it in all scopes, and promote it to current scope.
            foundTableExpression = PromoteTable(tableExpression, context);
            if (foundTableExpression != null)
            {
                return(foundTableExpression);
            }
            // 3. Add it
            context.CurrentSelect.Tables.Add(tableExpression);
            return(tableExpression);
        }
 protected virtual Expression GetOutputValueReader(Expression expression, Type expectedType, ParameterExpression dataRecordParameter, 
     ParameterExpression sessionParameter, TranslationContext context)
 {
     expectedType = expectedType ?? expression.Type;
       int valueIndex = RegisterOutputValue(expression, context);
       Func<object, object> conv = x => x;
       if(expression is ColumnExpression) {
     //With column everything is simple
     var colExpr = (ColumnExpression)expression;
     conv = colExpr.ColumnInfo.TypeInfo.ColumnToPropertyConverter;
       } else {
     //Otherwise get converter from type registry;
     // Why we need converters for non-column values.
     // Example: Count(*) function. In this case expectedType is always 'int', but some providers (MySql) return Int64.
     // In this case expression.Type is Int64 (it is SqlFunction expression), while expectedType is int32. We need a converter.
     // Note - even if expression.Type is the same as expectedType, we still might need a converter.
     // Example : 'Max(decimalCol)' in SQLite. SQLite stores decimals as doubles (we do), so SQL will return double;
     // we need an extra converter to cast to Decimal expected by c# code. We must go through TypeRegistry anyway,
     // to verify how type is supported.
     conv = _dbModel.Driver.TypeRegistry.GetLinqValueConverter(expression.Type, expectedType);
       }
       var reader = ColumnReaderHelper.GetColumnValueReader(dataRecordParameter, valueIndex, expectedType, conv);
       return reader;
 }
 /// <summary>
 /// Registers all columns of a table.
 /// </summary>
 /// <param name="tableExpression"></param>
 /// <param name="context"></param>
 /// <returns></returns>
 public virtual IList<ColumnExpression> RegisterAllColumns(TableExpression tableExpression, TranslationContext context)
 {
     var result = new List<ColumnExpression>();
       foreach (var colInfo in tableExpression.TableInfo.Columns)
       {
     if (colInfo.Member == null)
       continue;
     var colExpr = RegisterColumn(tableExpression, colInfo.Member.MemberName, context);
     result.Add(colExpr);
       }
       return result;
 }
        /// <summary>
        /// Promotes a table to a common parent between its current scope and our current scope
        /// </summary>
        /// <param name="tableExpression"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        protected virtual TableExpression PromoteTable(TableExpression tableExpression, TranslationContext context)
        {
            int currentIndex = 0;
            SelectExpression oldSelect = null;
            SelectExpression commonScope = null;
            TableExpression foundTable = null;
            do
            {
            // take a select
            oldSelect = context.SelectExpressions[currentIndex];

            // look for a common scope
            if (oldSelect != context.CurrentSelect)
            {
                commonScope = FindCommonScope(oldSelect, context.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 < context.SelectExpressions.Count && foundTable == null);

            if (foundTable != null)
            {
            oldSelect.Tables.Remove(foundTable);
            commonScope.Tables.Add(foundTable);
            }
            return foundTable;
        }
 private Expression BuildListPropertyJoinExpression(TableExpression fromTable, TableExpression toTable, EntityMemberInfo refMember, TranslationContext context)
 {
     var fromKeyMembers = refMember.ReferenceInfo.FromKey.ExpandedKeyMembers;
       var toKeyMembers = refMember.ReferenceInfo.ToKey.ExpandedKeyMembers;
       var clauses = new List<Expression>();
       for (int i = 0; i < fromKeyMembers.Count; i++) {
     var mFrom = fromKeyMembers[i];
     var mTo = toKeyMembers[i];
     var colFrom = RegisterColumn(fromTable, mFrom.Member.ClrClassMemberInfo, context);
     var colTo = RegisterColumn(toTable, mTo.Member.ClrClassMemberInfo, context);
     var eqExpr = MakeEqual(colFrom, colTo);
     clauses.Add(eqExpr);
       }
       //Build AND
       var result = clauses[0];
       for (int i = 1; i < clauses.Count; i++)
     result = Expression.And(result, clauses[i]);
       return result;
 }
        public virtual TableExpression RegisterAssociation(TableExpression tableExpression, EntityMemberInfo refMember, TranslationContext context)
        {
            IList<MemberInfo> otherKeys;
            TableJoinType joinType;
            string joinID;
            var theseKeys = GetAssociationMembers(tableExpression, refMember, out otherKeys, out joinType, out joinID);
            // if the memberInfo has no corresponding association, we get a null, that we propagate
            if (theseKeys == null)
            return null;

            // the current table has the foreign key, the other table the referenced (usually primary) key
            if (theseKeys.Count != otherKeys.Count)
            Util.Throw("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 otherType = refMember.DataType;
            var otherTableInfo = context.DbModel.GetTable(otherType);
            var otherTableExpression = CreateTable(otherType, context); // new TableExpression(otherType, otherTableInfo.FullName, otherTableInfo, joinID);
            otherTableExpression = RegisterTable(otherTableExpression, context);
            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], context);
            // foreign is created, we will store it later if this assocation is registered too
            Expression thisKey = CreateColumn(tableExpression, theseKeys[keyIndex].Name, context);
            createdColumns.Add((ColumnExpression)thisKey);

            // the other key is set as left operand, this must be this way
            // since some vendors (SQL Server) don't support the opposite
            var referenceExpression = MakeEqual(otherKey, thisKey);

            // 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
            var existingTable = (from t in context.EnumerateScopeTables() where t.IsEqualTo(otherTableExpression) select t).SingleOrDefault();
            if (existingTable != null)
            return existingTable;

            context.CurrentSelect.Tables.Add(otherTableExpression);
            foreach (var createdColumn in createdColumns)
            context.CurrentSelect.Columns.Add(createdColumn);
            return otherTableExpression;
        }
 /// <summary>
 /// Registers a where clause in the current context scope
 /// </summary>
 /// <param name="whereExpression"></param>
 /// <param name="context"></param>
 public virtual void RegisterWhere(Expression whereExpression, TranslationContext context)
 {
     var where = CheckBoolBitExpression(whereExpression);
     context.CurrentSelect.Where.Add(where);
 }
        private Expression AnalyzeEntityListMember(TableExpression table, PropertyInfo property, TranslationContext context)
        {
            var propType = property.PropertyType;
            if (!propType.IsEntitySequence())
              return null;
            var modelInfo = context.DbModel.EntityApp.Model;
            var masterEntInfo = modelInfo.GetEntityInfo(table.Type);
            var entMember = masterEntInfo.GetMember(property.Name);
            Util.Check(entMember != null, "Failed to find member {0} on entity {1}.", property.Name, masterEntInfo.Name);
            Util.Check(entMember.Kind == MemberKind.EntityList, "Internal LINQ error: expected List member ({0}.{1}", masterEntInfo.Name, property.Name);
            var listInfo = entMember.ChildListInfo;
            Expression whereExpr;
            switch(listInfo.RelationType) {

              case EntityRelationType.ManyToOne:
            var childTable = CreateTable(listInfo.TargetEntity.EntityType, context);
            whereExpr = BuildListPropertyJoinExpression(childTable, table, listInfo.ParentRefMember, context);
            if (!string.IsNullOrWhiteSpace(listInfo.Filter)) {
              var filterExpr = new TableFilterExpression(childTable, listInfo.Filter);
              whereExpr = Expression.And(whereExpr, filterExpr);
            }
            context.CurrentSelect.Where.Add(whereExpr);
            return childTable;

              case EntityRelationType.ManyToMany:
            var linkTable = CreateTable(listInfo.LinkEntity.EntityType, context);
            whereExpr = BuildListPropertyJoinExpression(linkTable, table, listInfo.ParentRefMember, context);
            context.CurrentSelect.Where.Add(whereExpr);
            var targetTable = RegisterAssociation(linkTable, listInfo.OtherEntityRefMember, context);
            return targetTable;
            }
            return null; //never happens
        }
 /// <summary>
 /// Registers a column
 /// This method requires the table to be already registered
 /// </summary>
 /// <param name="table"></param>
 /// <param name="name"></param>
 /// <param name="context"></param>
 /// <returns></returns>
 public ColumnExpression RegisterColumn(TableExpression table, string name, TranslationContext context)
 {
     var queryColumn = GetRegisteredColumn(table, name, context);
     if (queryColumn == null)
     {
     table = RegisterTable(table, context);
     queryColumn = CreateColumn(table, name, context);
     context.CurrentSelect.Columns.Add(queryColumn);
     }
     return queryColumn;
 }
 // Converts expression returning list of entities into expression returning list of primary keys.
 // ex: session.EntitySet<IBook>() -> session.EntitySet<IBook>().Select(b=>b.Id)
 // This is used for conversion of expressions involving entities - which SQL cannot process directly- into expressions over IDs
 // One example is entSet.Contains(ent) method - we convert it into entSet.Select(e => e.Id).Contains(ent.Id)
 private Expression BuildSelectPrimaryKeys(Expression expression, TranslationContext context)
 {
     var entType = expression.Type.GetGenericArguments()[0];
       // build lambda
       var entPrm = Expression.Parameter(entType, "p");
       var body = GetPrimaryKeyAccessor(entPrm, context);
       var pkType = body.Type;
       var lambda = Expression.Lambda(body, entPrm);
       var quotedLambda = Expression.Quote(lambda);
       //build Select method
       var genSelMethod = ExpressionUtil.QueryableSelectMethod.MakeGenericMethod(entType, pkType);
       // Special case - when entity set is list, ex: pub.Books property; If we want to put Queryable.Select over pub.Books, we must convert
       // the argument. It has no effect on SQL generation, it purely to satisfy type checking restraints of expression construction.
       if (!typeof(IQueryable).IsAssignableFrom(expression.Type)) {
     var asQMethod = ExpressionUtil.QueryableAsQueryableMethod.MakeGenericMethod(entType);
     expression = Expression.Call(null, asQMethod, expression);
       }
       var callSelect = Expression.Call(null, genSelMethod, expression, quotedLambda);
       return callSelect;
 }
 public ColumnExpression RegisterColumn(TableExpression tableExpression, MemberInfo memberInfo, TranslationContext context)
 {
     return RegisterColumn(tableExpression, memberInfo.Name, context);
 }
 private IList<Expression> ConvertContainsWithObject(IList<Expression> parameters, TranslationContext context)
 {
     var arg1Type = parameters[1].Type;
       if (arg1Type.IsDbPrimitive())
     return parameters;
       // we have List<Entity>.Contains - convert to using IDs
       var newParams = new List<Expression>();
       // for param 0 (entity sequence), add 'Select(e=>e.Id)'
       var prm0 = BuildSelectPrimaryKeys(parameters[0], context);
       //for parameter 1 (entity to select), add accessor to primary key
       var prm1 = GetPrimaryKeyAccessor(parameters[1], context);
       newParams.Add(prm0);
       newParams.Add(prm1);
       return newParams;
 }
 /// <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="context"></param>
 /// <returns>Expression index</returns>
 public virtual int RegisterOutputValue(Expression expression, TranslationContext context)
 {
     var scope = context.CurrentSelect;
     //check if expression is already registered
     var operands = scope.Operands.ToList();
     var index = operands.IndexOf(expression);
     if (index >= 0)
       return index;
     operands.Add(expression);
     context.CurrentSelect = (SelectExpression)scope.Mutate(operands);
     return operands.Count - 1;
 }
 // Converts expressions like 'book.Publisher.Id' to 'book.Publisher_id', to avoid unnecessary joins
 // Note that we cannot perform this optimization on original expression tree; the Publisher_id property is not available
 //  on IBook interface, so we must first convert IBook to book table, and then create member access expr 'BookTable.Publisher_id'
 private Expression OptimizeChainedMemberAccess(MemberExpression memberExpression, TranslationContext context)
 {
     var bookPubIdExpr = memberExpression; //just rename it for clarity
       var bookPubExpr = bookPubIdExpr.Expression as MemberExpression;
       var bookExpr = bookPubExpr.Expression;
       if (bookPubExpr.Member.IsStaticMember())
     return null;
       // check that child expr src object (book) is a Table
       var bookTable = context.DbModel.GetTable(bookExpr.Type, throwIfNotFound: false);
       if (bookTable == null)
     return null;
       // check that child expr result (book.Publisher) has Table result type
       var pubTable = context.DbModel.GetTable(bookPubExpr.Type, throwIfNotFound: false);
       if (pubTable == null)
     return null;
       //check that expr is trying read PK member of the target entity (Id of Publisher)
       var idMemberName = bookPubIdExpr.Member.Name;
       var idMember = pubTable.PrimaryKey.EntityKey.ExpandedKeyMembers.FirstOrDefault(km => km.Member.MemberName == idMemberName);
       if (idMember == null)
     return null;
       //OK, it might be a case for optimization. Analyze bottom parameter (Book reference)
       var analyzedBookExpr = Analyze(bookExpr, context);
       // Don't optimize it if it is based on input parameter; just return derived parameter expression.
       // note: we cannot return null here, it is too late - bookExpr had been already analyzed, it will mess it all
       if (analyzedBookExpr is ExternalValueExpression) {
     var bookPubPrm = DeriveMemberAccessParameter((ExternalValueExpression)analyzedBookExpr, bookPubExpr.Member, context);
     var bookPubIdPrm = DeriveMemberAccessParameter(bookPubPrm, bookPubIdExpr.Member, context);
     return bookPubIdPrm;
       }
       // Find FK member on src table (Publisher_id on book entity)
       var idMemberIndex = pubTable.PrimaryKey.EntityKey.ExpandedKeyMembers.IndexOf(idMember);
       var pubRefMember = bookPubExpr.Member;
       var pubRefMemberInfo = bookTable.Entity.Members.First(m => m.ClrMemberInfo == pubRefMember);
       var bookPubIdMemberInfo = pubRefMemberInfo.ReferenceInfo.FromKey.ExpandedKeyMembers[idMemberIndex].Member;
       ColumnExpression queryColumnExpression = RegisterColumn((TableExpression)analyzedBookExpr, bookPubIdMemberInfo.ClrClassMemberInfo, context);
       return queryColumnExpression;
 }
 /// <summary>
 /// Returns an existing table or registers the current one
 /// </summary>
 /// <param name="tableExpression"></param>
 /// <param name="context"></param>
 /// <returns>A registered table or the current newly registered one</returns>
 public virtual TableExpression RegisterTable(TableExpression tableExpression, TranslationContext context)
 {
     // 1. Find the table in current scope
     var foundTableExpression = (from t in context.EnumerateScopeTables()
                             where t.IsEqualTo(tableExpression)
                             select t).FirstOrDefault();
     if (foundTableExpression != null)
     return foundTableExpression;
     // 2. Find it in all scopes, and promote it to current scope.
     foundTableExpression = PromoteTable(tableExpression, context);
     if (foundTableExpression != null)
     return foundTableExpression;
     // 3. Add it
     context.CurrentSelect.Tables.Add(tableExpression);
     return tableExpression;
 }
Example #37
0
        private TranslatedLinqCommand TranslateNonQuery(LinqCommand command)
        {
            LinqCommandPreprocessor.PreprocessCommand(_dbModel.EntityApp.Model, command);
              var rewriterContext = new TranslationContext(_dbModel, command);
              var cmdInfo = command.Info;
              // convert lambda params into an initial set of ExternalValueExpression objects;
              foreach(var prm in cmdInfo.Lambda.Parameters) {
            var inpParam = new ExternalValueExpression(prm);
            rewriterContext.ExternalValues.Add(inpParam);
              }
              //Analyze/transform base select query
              var exprChain = ExpressionChain.Build(cmdInfo.Lambda.Body);
              var selectExpr = BuildSelectExpression(exprChain, rewriterContext);
              // Analyze external values (parameters?), create DbParameters
              var cmdParams = BuildParameters(command, rewriterContext);
              var flags = command.Info.Flags;
              // If there's at least one parameter that must be converted to literal (ex: value list), we cannot cache the query
              bool canCache = !rewriterContext.ExternalValues.Any(v => v.SqlUse == ExternalValueSqlUse.Literal);
              if (!canCache)
            flags |= LinqCommandFlags.NoQueryCache;

              // !!! Before that, everyting is the same as in TranslateSelect
              var targetEnt = command.TargetEntity;
              var targetTableInfo = _dbModel.GetTable(targetEnt.EntityType);
              TableExpression targetTable;
              bool isSingleTable = selectExpr.Tables.Count == 1 && selectExpr.Tables[0].TableInfo == targetTableInfo;
              if(isSingleTable) {
            targetTable = selectExpr.Tables[0];
              } else
            targetTable = _translator.CreateTable(targetEnt.EntityType, rewriterContext);
              var commandData = new NonQueryLinqCommandData(command, selectExpr, targetTable, isSingleTable);
              // Analyze base query output expression
              var readerBody = selectExpr.Reader.Body;
              switch(command.CommandType) {
            case LinqCommandType.Update:
            case LinqCommandType.Insert:
              Util.Check(readerBody.NodeType == ExpressionType.New, "Query for LINQ {0} command must return New object", commandData.CommandType);
              var newExpr = readerBody as NewExpression;
              var outValues = selectExpr.Operands.ToList();
              for(int i = 0; i < newExpr.Members.Count; i++) {
            var memberName = newExpr.Members[i].Name;
            var memberInfo = targetEnt.GetMember(memberName);
            Util.Check(memberInfo != null, "Member {0} not found in entity {1}.", memberName, targetEnt, targetEnt.EntityType);
            switch(memberInfo.Kind) {
              case MemberKind.Column:
                var col = _translator.CreateColumn(targetTable, memberName, rewriterContext);
                commandData.TargetColumns.Add(col);
                commandData.SelectOutputValues.Add(outValues[i]);
                break;
              case MemberKind.EntityRef:
                var fromKey = memberInfo.ReferenceInfo.FromKey;
                Util.Check(fromKey.ExpandedKeyMembers.Count == 1,
                  "References with composite keys are not supported in LINQ non-query operations. Reference: ", memberName);
                var pkMember = fromKey.ExpandedKeyMembers[0].Member;
                var col2 = _translator.CreateColumn(targetTable, pkMember.MemberName, rewriterContext);
                commandData.TargetColumns.Add(col2);
                commandData.SelectOutputValues.Add(outValues[i]);
                break;
              default:
                Util.Throw("Property cannot be used in the context: {0}.", memberName);
                break;
            }
              }
              break;
            case LinqCommandType.Delete:
              commandData.SelectOutputValues.Add(readerBody); //should return single value - primary key
              break;
              }
              // Build SQL
              var sqlBuilder = new SqlBuilder(_dbModel);
              var sqlStmt = sqlBuilder.BuildNonQuery(commandData);
              var sqlTemplate = sqlStmt.ToString();
              var defaultSql = FormatSql(sqlTemplate, cmdParams);
              return new TranslatedLinqCommand(sqlTemplate, defaultSql, cmdParams, flags);
        }
 public ColumnExpression RegisterColumn(TableExpression tableExpression, MemberInfo memberInfo, TranslationContext context)
 {
     return(RegisterColumn(tableExpression, memberInfo.Name, context));
 }