/// <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;
 }
        /// <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;
        }