Sql8ExpressionRewriter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / SqlClient / SqlGen / Sql8ExpressionRewriter.cs / 1 / Sql8ExpressionRewriter.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization;

using System.Data.Common; 
using System.Data.Common.CommandTrees;
using System.Data.Common.CommandTrees.Internal; 
 
using System.Data.Metadata.Edm;
 
namespace System.Data.SqlClient.SqlGen
{
    /// 
    /// Rewrites an expression tree to make it suitable for translation to SQL appropriate for SQL Server 2000 
    /// In particular, it replaces expressions that are not directly supported on SQL Server 2000
    /// with alternative translations. The following expressions are translated: 
    ///  
    /// 
    ///  
    /// 
    /// 
    ///
    /// The other expressions are copied unmodified. 
    /// The new expression belongs to a new query command tree.
    ///  
    internal class Sql8ExpressionRewriter : ExpressionCopier 
    {
        #region Entry Point 
        /// 
        /// The only entry point.
        /// Rewrites the given tree by replacing expressions that are not directly supported on SQL Server 2000
        /// with alterntive translations. 
        /// 
        /// The tree to rewrite 
        /// The new tree 
        internal static DbQueryCommandTree Rewrite(DbQueryCommandTree originalTree)
        { 
            Debug.Assert(originalTree != null, "OriginalTree is null");
            DbQueryCommandTree newTree = new DbQueryCommandTree(originalTree.MetadataWorkspace, originalTree.DataSpace);
            originalTree.CopyParametersTo(newTree);
            Sql8ExpressionRewriter rewriter = new Sql8ExpressionRewriter(newTree); 
            newTree.Query = rewriter.VisitExpr(originalTree.Query);
            return newTree; 
        } 
        #endregion
 
        #region Constructor
        /// 
        /// Private Constructor.
        ///  
        /// 
        private Sql8ExpressionRewriter(DbQueryCommandTree newTree) 
            :base(newTree, MetadataMapper.IdentityMapper) 
        {
        } 
        #endregion

        #region DbExpressionVisitor Members
        ///  
        /// 
        ///  
        ///  
        /// 
        public override DbExpression Visit(DbExceptExpression e) 
        {
            return TransformIntersectOrExcept(VisitExpr(e.Left), VisitExpr(e.Right), DbExpressionKind.Except);
        }
 
        /// 
        ///  
        ///  
        /// 
        ///  
        public override DbExpression Visit(DbIntersectExpression e)
        {
            return TransformIntersectOrExcept(VisitExpr(e.Left), VisitExpr(e.Right), DbExpressionKind.Intersect);
        } 

        ///  
        /// Logicaly,  translates to: 
        /// SELECT Y.x1, Y.x2, ..., Y.xn
        /// FROM ( 
        ///     SELECT X.x1, X.x2, ..., X.xn,
        ///     FROM input AS X
        ///        EXCEPT
        ///     SELECT TOP(count) Z.x1, Z.x2, ..., Z.xn 
        ///     FROM input AS Z
        ///     ORDER BY sk1, sk2, ... 
        ///     ) AS Y 
        /// ORDER BY sk1, sk2, ...
        /// 
        /// Here, input refers to the input of the , and count to the count property of the .
        /// The implementation of EXCEPT is non-duplicate eliminating, and does equality comparison only over the
        /// equality comparable columns of the input.
        /// 
        /// This corresponds to the following expression tree:
        /// 
        /// SORT 
        ///  |
        /// NON-DISTINCT EXCEPT  (specially translated,  
        ///  |
        ///  | - Left:  clone of input
        ///  | - Right:
        ///       | 
        ///      Limit
        ///       | 
        ///       | - Limit: Count 
        ///       | - Input
        ///             | 
        ///            Sort
        ///             |
        ///            input
        /// 
        /// 
        ///  
        ///  
        public override DbExpression Visit(DbSkipExpression e)
        { 
            //Build the right input of the except
            DbExpression rightInput = CommandTree.CreateLimitExpression(
                CommandTree.CreateSortExpression(
                    VisitBinding(e.Input), 
                    VisitSortOrder(e.SortOrder)),
                VisitExpr(e.Count)); 
 
            //Build the left input for the except
            DbExpression leftInput = VisitExpr(e.Input.Expression); //Another copy of the input 

            List sortOrder = VisitSortOrder(e.SortOrder); //Another copy of the sort order

            // Create a list of the sort expressions to be used for translating except 
            IList sortExpressions = new List(e.SortOrder.Count);
            foreach (DbSortClause sortClause in sortOrder) 
            { 
                //We only care about property expressions, not about constants
                if (sortClause.Expression.ExpressionKind == DbExpressionKind.Property) 
                {
                    sortExpressions.Add((DbPropertyExpression)sortClause.Expression);
                }
            } 

            DbExpression exceptExpression = TransformIntersectOrExcept(leftInput, rightInput, DbExpressionKind.Skip, sortExpressions, e.Input.VariableName); 
 
            DbExpression result = CommandTree.CreateSortExpression(
                CommandTree.CreateExpressionBinding(exceptExpression, e.Input.VariableName), 
                sortOrder);

            return result;
        } 
        #endregion
 
        #region DbExpressionVisitor Member Helpers 

        ///  
        /// This method is invoked when tranforming  and  by doing comparison over all input columns.
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        private DbExpression TransformIntersectOrExcept(DbExpression left, DbExpression right, DbExpressionKind expressionKind)
        { 
            return TransformIntersectOrExcept( left,  right,  expressionKind, null, null);
        }

        ///  
        /// This method is used for translating  and ,
        /// and for translating the "Except" part of . 
        /// into the follwoing expression: 
        ///
        /// A INTERSECT B, A EXCEPT B 
        ///
        /// (DISTINCT)
        ///  |
        /// FILTER 
        ///  |
        ///  | - Input: A 
        ///  | - Predicate:(NOT) 
        ///                 |
        ///                 ANY 
        ///                 |
        ///                 | - Input: B
        ///                 | - Predicate:  (B.b1 = A.a1 or (B.b1 is null and A.a1 is null))
        ///                             AND (B.b2 = A.a2 or (B.b2 is null and A.a2 is null)) 
        ///                             AND ...
        ///                             AND (B.bn = A.an or (B.bn is null and A.an is null))) 
        /// 
        /// Here, A corresponds to right and B to left.
        /// (NOT) is present when transforming Except 
        /// for the purpose of translating  or .
        /// (DISTINCT) is present when transforming for the purpose of translating
        ///  or .
        /// 
        /// For , the input to ANY is caped with project which projects out only
        /// the columns represented in the sortExpressionsOverLeft list and only these are used in the predicate. 
        /// This is because we want to support skip over input with non-equal comarable columns and we have no way to recognize these. 
        /// 
        ///  
        /// 
        /// 
        /// note that this list gets destroyed by this method
        ///  
        /// 
        private DbExpression TransformIntersectOrExcept(DbExpression left, DbExpression right, DbExpressionKind expressionKind, IList sortExpressionsOverLeft, string sortExpressionsBindingVariableName) 
        { 
            bool negate = (expressionKind == DbExpressionKind.Except) || (expressionKind == DbExpressionKind.Skip);
            bool distinct = (expressionKind == DbExpressionKind.Except) || (expressionKind == DbExpressionKind.Intersect); 

            DbExpressionBinding leftInputBinding = this.CommandTree.CreateExpressionBinding(left);
            DbExpressionBinding rightInputBinding = this.CommandTree.CreateExpressionBinding(right);
 
            IList leftFlattenedProperties = new List();
            IList rightFlattenedProperties = new List(); 
 
            FlattenProperties(leftInputBinding.Variable, leftFlattenedProperties);
            FlattenProperties(rightInputBinding.Variable, rightFlattenedProperties); 

            //For Skip, we need to ignore any columns that are not in the original sort list. We can recognize these by comparing the left flattened properties and
            // the properties in the list sortExpressionsOverLeft
            // If any such columns exist, we need to add an additional project, to keep the rest of the columns from being projected, as if any among these 
            // are non equal comparable, SQL Server 2000 throws.
            if (expressionKind == DbExpressionKind.Skip) 
            { 
                if (RemoveNonSortProperties(leftFlattenedProperties, rightFlattenedProperties, sortExpressionsOverLeft, leftInputBinding.VariableName, sortExpressionsBindingVariableName))
                { 
                   rightInputBinding = CapWithProject(rightInputBinding, rightFlattenedProperties);
                }
            }
 
            Debug.Assert(leftFlattenedProperties.Count == rightFlattenedProperties.Count, "The left and the right input to INTERSECT or EXCEPT have a different number of properties");
            Debug.Assert(leftFlattenedProperties.Count != 0, "The inputs to INTERSECT or EXCEPT have no properties"); 
 
            //Build the predicate for the quantifier:
            //   (B.b1 = A.a1 or (B.b1 is null and A.a1 is null)) 
            //      AND (B.b2 = A.a2 or (B.b2 is null and A.a2 is null))
            //      AND ...
            //      AND (B.bn = A.an or (B.bn is null and A.an is null)))
            DbExpression existsPredicate = null; 

            for (int i = 0; i < leftFlattenedProperties.Count; i++) 
            { 
                //A.ai == B.bi
                DbExpression equalsExpression = CommandTree.CreateEqualsExpression(leftFlattenedProperties[i], rightFlattenedProperties[i]); 

                //A.ai is null AND B.bi is null
                DbExpression leftIsNullExpression = CommandTree.CreateIsNullExpression(leftFlattenedProperties[i].Clone() as DbExpression);
                DbExpression rightIsNullExpression = CommandTree.CreateIsNullExpression(rightFlattenedProperties[i].Clone() as DbExpression); 
                DbExpression bothNullExpression = CommandTree.CreateAndExpression(leftIsNullExpression, rightIsNullExpression);
 
                DbExpression orExpression = CommandTree.CreateOrExpression(equalsExpression, bothNullExpression); 

                if (i == 0) 
                {
                    existsPredicate = orExpression;
                }
                else 
                {
                    existsPredicate = CommandTree.CreateAndExpression(existsPredicate, orExpression); 
                } 
            }
 
            //Build the quantifier
            DbExpression quantifierExpression = CommandTree.CreateAnyExpression(rightInputBinding, existsPredicate);

            DbExpression filterPredicate; 

            //Negate if needed 
            if (negate) 
            {
                filterPredicate = CommandTree.CreateNotExpression(quantifierExpression); 
            }
            else
            {
                filterPredicate = quantifierExpression; 
            }
 
            //Build the filter 
            DbExpression result = CommandTree.CreateFilterExpression(leftInputBinding, filterPredicate);
 
            //Apply distinct in needed
            if (distinct)
            {
                result = CommandTree.CreateDistinctExpression(result); 
            }
 
            return result; 
        }
 
        /// 
        /// Adds the flattened properties on the input to the flattenedProperties list.
        /// 
        ///  
        /// 
        private void FlattenProperties(DbExpression input, IList flattenedProperties) 
        { 
            IList properties = TypeHelpers.GetProperties(input.ResultType);
            Debug.Assert(properties.Count != 0, "No nested properties when FlattenProperties called?"); 

            for (int i = 0; i < properties.Count; i++)
            {
                DbExpression propertyInput = (i == 0) ? input : (input.Clone() as DbExpression); 

                DbPropertyExpression propertyExpression = CommandTree.CreatePropertyExpression(properties[i], propertyInput); 
                if (TypeSemantics.IsPrimitiveType(properties[i].TypeUsage)) 
                {
                    flattenedProperties.Add(propertyExpression); 
                }
                else
                {
                    Debug.Assert(TypeSemantics.IsEntityType(properties[i].TypeUsage) || TypeSemantics.IsRowType(properties[i].TypeUsage), 
                        "The input to FlattenProperties is not of EntityType or RowType?");
 
                    FlattenProperties(propertyExpression, flattenedProperties); 
                }
            } 
        }


        ///  
        /// Helper method for 
        /// Removes all pairs of property expressions from list1 and list2, for which the property expression in list1 
        /// does not have a 'matching' property expression in list2. 
        /// The lists list1 and list2 are known to not create duplicate, and the purpose of the sortList is just for this method.
        /// Thus, to optimize the match process, we remove the seen property expressions from the sort list in  
        /// when iterating both list simultaneously.
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        /// 
        private static bool RemoveNonSortProperties(IList list1, IList list2, IList sortList, string list1BindingVariableName, string sortExpressionsBindingVariableName) 
        {
            bool result = false;
            for (int i = list1.Count - 1; i >= 0; i--)
            { 
                if (!HasMatchInList(list1[i], sortList, list1BindingVariableName, sortExpressionsBindingVariableName))
                { 
                    list1.RemoveAt(i); 
                    list2.RemoveAt(i);
                    result = true; 
                }
            }
            return result;
        } 

        ///  
        /// Helper method for  
        /// Checks whether expr has a 'match' in the given list of property expressions.
        /// If it does, the matching expression is removed form the list, to speed up future matching. 
        /// 
        /// 
        /// 
        ///  
        /// 
        ///  
        private static bool HasMatchInList(DbPropertyExpression expr, IList list, string exprBindingVariableName, string listExpressionsBindingVariableName) 
        {
            for (int i=0; i 
        /// Determines whether two expressions match.
        /// They match if they are  of the shape 
        ///   expr1 -> DbPropertyExpression(... (DbPropertyExpression(DbVariableReferenceExpression(expr1BindingVariableName), nameX), ..., name1) 
        ///   expr1 -> DbPropertyExpression(... (DbPropertyExpression(DbVariableReferenceExpression(expr2BindingVariableName), nameX), ..., name1),
        /// 
        /// i.e. if they only differ in the name of the binding.
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        private static bool AreMatching(DbPropertyExpression expr1, DbPropertyExpression expr2, string expr1BindingVariableName, string expr2BindingVariableName)
        { 
            if (expr1.Property.Name != expr2.Property.Name)
            {
                return false;
            } 

            if (expr1.Instance.ExpressionKind != expr2.Instance.ExpressionKind) 
            { 
                return false;
            } 

            if (expr1.Instance.ExpressionKind == DbExpressionKind.Property)
            {
                return AreMatching((DbPropertyExpression)expr1.Instance, (DbPropertyExpression)expr2.Instance, expr1BindingVariableName, expr2BindingVariableName); 
            }
 
            DbVariableReferenceExpression instance1 =  (DbVariableReferenceExpression)expr1.Instance; 
            DbVariableReferenceExpression instance2 =  (DbVariableReferenceExpression)expr2.Instance;
 
            return (String.Equals(instance1.VariableName, expr1BindingVariableName, StringComparison.Ordinal)
                && String.Equals(instance2.VariableName, expr2BindingVariableName, StringComparison.Ordinal));
        }
 
        /// 
        /// Helper method for  
        /// Creates a  over the given inputBinding that projects out the given flattenedProperties. 
        /// and updates the flattenedProperties to be over the newly created project.
        ///  
        /// 
        /// 
        /// An  over the newly created 
        private DbExpressionBinding CapWithProject(DbExpressionBinding inputBinding, IList flattenedProperties) 
        {
            List> projectColumns = new List>(flattenedProperties.Count); 
 
            //List of all the columnNames used in the projection.
            Dictionary columnNames = new Dictionary(flattenedProperties.Count); 

            foreach (DbPropertyExpression pe in flattenedProperties)
            {
                //There may be conflicting property names, thus we may need to rename. 
                string name = pe.Property.Name;
                int i; 
                if (columnNames.TryGetValue(name, out i)) 
                {
                    string newName; 
                    do
                    {
                        ++i;
                        newName = name + i.ToString(System.Globalization.CultureInfo.InvariantCulture); 
                    } while (columnNames.ContainsKey(newName));
 
                    columnNames[name] = i; 
                    name = newName;
                } 

                // Add this column name to list of known names so that there are no subsequent
                // collisions
                columnNames[name] = 0; 
                projectColumns.Add(new KeyValuePair(name, pe));
            } 
 
            //Build the project
            DbExpression rowExpr = CommandTree.CreateNewRowExpression(projectColumns); 
            DbProjectExpression projectExpression = CommandTree.CreateProjectExpression(inputBinding, rowExpr);

            //Create the new inputBinding
            DbExpressionBinding resultBinding = this.CommandTree.CreateExpressionBinding(projectExpression); 

            //Create the list of flattenedProperties over the new project 
            flattenedProperties.Clear(); 
            RowType rowExprType = (RowType)rowExpr.ResultType.EdmType;
 
            foreach (KeyValuePair column in projectColumns)
            {
                EdmProperty prop = rowExprType.Properties[column.Key];
                flattenedProperties.Add( 
                    CommandTree.CreatePropertyExpression(
                    prop, 
                    CommandTree.CreateVariableReferenceExpression(resultBinding.VariableName, resultBinding.VariableType))); 
            }
            return resultBinding; 
        }

        #endregion
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization;

using System.Data.Common; 
using System.Data.Common.CommandTrees;
using System.Data.Common.CommandTrees.Internal; 
 
using System.Data.Metadata.Edm;
 
namespace System.Data.SqlClient.SqlGen
{
    /// 
    /// Rewrites an expression tree to make it suitable for translation to SQL appropriate for SQL Server 2000 
    /// In particular, it replaces expressions that are not directly supported on SQL Server 2000
    /// with alternative translations. The following expressions are translated: 
    ///  
    /// 
    ///  
    /// 
    /// 
    ///
    /// The other expressions are copied unmodified. 
    /// The new expression belongs to a new query command tree.
    ///  
    internal class Sql8ExpressionRewriter : ExpressionCopier 
    {
        #region Entry Point 
        /// 
        /// The only entry point.
        /// Rewrites the given tree by replacing expressions that are not directly supported on SQL Server 2000
        /// with alterntive translations. 
        /// 
        /// The tree to rewrite 
        /// The new tree 
        internal static DbQueryCommandTree Rewrite(DbQueryCommandTree originalTree)
        { 
            Debug.Assert(originalTree != null, "OriginalTree is null");
            DbQueryCommandTree newTree = new DbQueryCommandTree(originalTree.MetadataWorkspace, originalTree.DataSpace);
            originalTree.CopyParametersTo(newTree);
            Sql8ExpressionRewriter rewriter = new Sql8ExpressionRewriter(newTree); 
            newTree.Query = rewriter.VisitExpr(originalTree.Query);
            return newTree; 
        } 
        #endregion
 
        #region Constructor
        /// 
        /// Private Constructor.
        ///  
        /// 
        private Sql8ExpressionRewriter(DbQueryCommandTree newTree) 
            :base(newTree, MetadataMapper.IdentityMapper) 
        {
        } 
        #endregion

        #region DbExpressionVisitor Members
        ///  
        /// 
        ///  
        ///  
        /// 
        public override DbExpression Visit(DbExceptExpression e) 
        {
            return TransformIntersectOrExcept(VisitExpr(e.Left), VisitExpr(e.Right), DbExpressionKind.Except);
        }
 
        /// 
        ///  
        ///  
        /// 
        ///  
        public override DbExpression Visit(DbIntersectExpression e)
        {
            return TransformIntersectOrExcept(VisitExpr(e.Left), VisitExpr(e.Right), DbExpressionKind.Intersect);
        } 

        ///  
        /// Logicaly,  translates to: 
        /// SELECT Y.x1, Y.x2, ..., Y.xn
        /// FROM ( 
        ///     SELECT X.x1, X.x2, ..., X.xn,
        ///     FROM input AS X
        ///        EXCEPT
        ///     SELECT TOP(count) Z.x1, Z.x2, ..., Z.xn 
        ///     FROM input AS Z
        ///     ORDER BY sk1, sk2, ... 
        ///     ) AS Y 
        /// ORDER BY sk1, sk2, ...
        /// 
        /// Here, input refers to the input of the , and count to the count property of the .
        /// The implementation of EXCEPT is non-duplicate eliminating, and does equality comparison only over the
        /// equality comparable columns of the input.
        /// 
        /// This corresponds to the following expression tree:
        /// 
        /// SORT 
        ///  |
        /// NON-DISTINCT EXCEPT  (specially translated,  
        ///  |
        ///  | - Left:  clone of input
        ///  | - Right:
        ///       | 
        ///      Limit
        ///       | 
        ///       | - Limit: Count 
        ///       | - Input
        ///             | 
        ///            Sort
        ///             |
        ///            input
        /// 
        /// 
        ///  
        ///  
        public override DbExpression Visit(DbSkipExpression e)
        { 
            //Build the right input of the except
            DbExpression rightInput = CommandTree.CreateLimitExpression(
                CommandTree.CreateSortExpression(
                    VisitBinding(e.Input), 
                    VisitSortOrder(e.SortOrder)),
                VisitExpr(e.Count)); 
 
            //Build the left input for the except
            DbExpression leftInput = VisitExpr(e.Input.Expression); //Another copy of the input 

            List sortOrder = VisitSortOrder(e.SortOrder); //Another copy of the sort order

            // Create a list of the sort expressions to be used for translating except 
            IList sortExpressions = new List(e.SortOrder.Count);
            foreach (DbSortClause sortClause in sortOrder) 
            { 
                //We only care about property expressions, not about constants
                if (sortClause.Expression.ExpressionKind == DbExpressionKind.Property) 
                {
                    sortExpressions.Add((DbPropertyExpression)sortClause.Expression);
                }
            } 

            DbExpression exceptExpression = TransformIntersectOrExcept(leftInput, rightInput, DbExpressionKind.Skip, sortExpressions, e.Input.VariableName); 
 
            DbExpression result = CommandTree.CreateSortExpression(
                CommandTree.CreateExpressionBinding(exceptExpression, e.Input.VariableName), 
                sortOrder);

            return result;
        } 
        #endregion
 
        #region DbExpressionVisitor Member Helpers 

        ///  
        /// This method is invoked when tranforming  and  by doing comparison over all input columns.
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        private DbExpression TransformIntersectOrExcept(DbExpression left, DbExpression right, DbExpressionKind expressionKind)
        { 
            return TransformIntersectOrExcept( left,  right,  expressionKind, null, null);
        }

        ///  
        /// This method is used for translating  and ,
        /// and for translating the "Except" part of . 
        /// into the follwoing expression: 
        ///
        /// A INTERSECT B, A EXCEPT B 
        ///
        /// (DISTINCT)
        ///  |
        /// FILTER 
        ///  |
        ///  | - Input: A 
        ///  | - Predicate:(NOT) 
        ///                 |
        ///                 ANY 
        ///                 |
        ///                 | - Input: B
        ///                 | - Predicate:  (B.b1 = A.a1 or (B.b1 is null and A.a1 is null))
        ///                             AND (B.b2 = A.a2 or (B.b2 is null and A.a2 is null)) 
        ///                             AND ...
        ///                             AND (B.bn = A.an or (B.bn is null and A.an is null))) 
        /// 
        /// Here, A corresponds to right and B to left.
        /// (NOT) is present when transforming Except 
        /// for the purpose of translating  or .
        /// (DISTINCT) is present when transforming for the purpose of translating
        ///  or .
        /// 
        /// For , the input to ANY is caped with project which projects out only
        /// the columns represented in the sortExpressionsOverLeft list and only these are used in the predicate. 
        /// This is because we want to support skip over input with non-equal comarable columns and we have no way to recognize these. 
        /// 
        ///  
        /// 
        /// 
        /// note that this list gets destroyed by this method
        ///  
        /// 
        private DbExpression TransformIntersectOrExcept(DbExpression left, DbExpression right, DbExpressionKind expressionKind, IList sortExpressionsOverLeft, string sortExpressionsBindingVariableName) 
        { 
            bool negate = (expressionKind == DbExpressionKind.Except) || (expressionKind == DbExpressionKind.Skip);
            bool distinct = (expressionKind == DbExpressionKind.Except) || (expressionKind == DbExpressionKind.Intersect); 

            DbExpressionBinding leftInputBinding = this.CommandTree.CreateExpressionBinding(left);
            DbExpressionBinding rightInputBinding = this.CommandTree.CreateExpressionBinding(right);
 
            IList leftFlattenedProperties = new List();
            IList rightFlattenedProperties = new List(); 
 
            FlattenProperties(leftInputBinding.Variable, leftFlattenedProperties);
            FlattenProperties(rightInputBinding.Variable, rightFlattenedProperties); 

            //For Skip, we need to ignore any columns that are not in the original sort list. We can recognize these by comparing the left flattened properties and
            // the properties in the list sortExpressionsOverLeft
            // If any such columns exist, we need to add an additional project, to keep the rest of the columns from being projected, as if any among these 
            // are non equal comparable, SQL Server 2000 throws.
            if (expressionKind == DbExpressionKind.Skip) 
            { 
                if (RemoveNonSortProperties(leftFlattenedProperties, rightFlattenedProperties, sortExpressionsOverLeft, leftInputBinding.VariableName, sortExpressionsBindingVariableName))
                { 
                   rightInputBinding = CapWithProject(rightInputBinding, rightFlattenedProperties);
                }
            }
 
            Debug.Assert(leftFlattenedProperties.Count == rightFlattenedProperties.Count, "The left and the right input to INTERSECT or EXCEPT have a different number of properties");
            Debug.Assert(leftFlattenedProperties.Count != 0, "The inputs to INTERSECT or EXCEPT have no properties"); 
 
            //Build the predicate for the quantifier:
            //   (B.b1 = A.a1 or (B.b1 is null and A.a1 is null)) 
            //      AND (B.b2 = A.a2 or (B.b2 is null and A.a2 is null))
            //      AND ...
            //      AND (B.bn = A.an or (B.bn is null and A.an is null)))
            DbExpression existsPredicate = null; 

            for (int i = 0; i < leftFlattenedProperties.Count; i++) 
            { 
                //A.ai == B.bi
                DbExpression equalsExpression = CommandTree.CreateEqualsExpression(leftFlattenedProperties[i], rightFlattenedProperties[i]); 

                //A.ai is null AND B.bi is null
                DbExpression leftIsNullExpression = CommandTree.CreateIsNullExpression(leftFlattenedProperties[i].Clone() as DbExpression);
                DbExpression rightIsNullExpression = CommandTree.CreateIsNullExpression(rightFlattenedProperties[i].Clone() as DbExpression); 
                DbExpression bothNullExpression = CommandTree.CreateAndExpression(leftIsNullExpression, rightIsNullExpression);
 
                DbExpression orExpression = CommandTree.CreateOrExpression(equalsExpression, bothNullExpression); 

                if (i == 0) 
                {
                    existsPredicate = orExpression;
                }
                else 
                {
                    existsPredicate = CommandTree.CreateAndExpression(existsPredicate, orExpression); 
                } 
            }
 
            //Build the quantifier
            DbExpression quantifierExpression = CommandTree.CreateAnyExpression(rightInputBinding, existsPredicate);

            DbExpression filterPredicate; 

            //Negate if needed 
            if (negate) 
            {
                filterPredicate = CommandTree.CreateNotExpression(quantifierExpression); 
            }
            else
            {
                filterPredicate = quantifierExpression; 
            }
 
            //Build the filter 
            DbExpression result = CommandTree.CreateFilterExpression(leftInputBinding, filterPredicate);
 
            //Apply distinct in needed
            if (distinct)
            {
                result = CommandTree.CreateDistinctExpression(result); 
            }
 
            return result; 
        }
 
        /// 
        /// Adds the flattened properties on the input to the flattenedProperties list.
        /// 
        ///  
        /// 
        private void FlattenProperties(DbExpression input, IList flattenedProperties) 
        { 
            IList properties = TypeHelpers.GetProperties(input.ResultType);
            Debug.Assert(properties.Count != 0, "No nested properties when FlattenProperties called?"); 

            for (int i = 0; i < properties.Count; i++)
            {
                DbExpression propertyInput = (i == 0) ? input : (input.Clone() as DbExpression); 

                DbPropertyExpression propertyExpression = CommandTree.CreatePropertyExpression(properties[i], propertyInput); 
                if (TypeSemantics.IsPrimitiveType(properties[i].TypeUsage)) 
                {
                    flattenedProperties.Add(propertyExpression); 
                }
                else
                {
                    Debug.Assert(TypeSemantics.IsEntityType(properties[i].TypeUsage) || TypeSemantics.IsRowType(properties[i].TypeUsage), 
                        "The input to FlattenProperties is not of EntityType or RowType?");
 
                    FlattenProperties(propertyExpression, flattenedProperties); 
                }
            } 
        }


        ///  
        /// Helper method for 
        /// Removes all pairs of property expressions from list1 and list2, for which the property expression in list1 
        /// does not have a 'matching' property expression in list2. 
        /// The lists list1 and list2 are known to not create duplicate, and the purpose of the sortList is just for this method.
        /// Thus, to optimize the match process, we remove the seen property expressions from the sort list in  
        /// when iterating both list simultaneously.
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        /// 
        private static bool RemoveNonSortProperties(IList list1, IList list2, IList sortList, string list1BindingVariableName, string sortExpressionsBindingVariableName) 
        {
            bool result = false;
            for (int i = list1.Count - 1; i >= 0; i--)
            { 
                if (!HasMatchInList(list1[i], sortList, list1BindingVariableName, sortExpressionsBindingVariableName))
                { 
                    list1.RemoveAt(i); 
                    list2.RemoveAt(i);
                    result = true; 
                }
            }
            return result;
        } 

        ///  
        /// Helper method for  
        /// Checks whether expr has a 'match' in the given list of property expressions.
        /// If it does, the matching expression is removed form the list, to speed up future matching. 
        /// 
        /// 
        /// 
        ///  
        /// 
        ///  
        private static bool HasMatchInList(DbPropertyExpression expr, IList list, string exprBindingVariableName, string listExpressionsBindingVariableName) 
        {
            for (int i=0; i 
        /// Determines whether two expressions match.
        /// They match if they are  of the shape 
        ///   expr1 -> DbPropertyExpression(... (DbPropertyExpression(DbVariableReferenceExpression(expr1BindingVariableName), nameX), ..., name1) 
        ///   expr1 -> DbPropertyExpression(... (DbPropertyExpression(DbVariableReferenceExpression(expr2BindingVariableName), nameX), ..., name1),
        /// 
        /// i.e. if they only differ in the name of the binding.
        /// 
        /// 
        ///  
        /// 
        ///  
        ///  
        private static bool AreMatching(DbPropertyExpression expr1, DbPropertyExpression expr2, string expr1BindingVariableName, string expr2BindingVariableName)
        { 
            if (expr1.Property.Name != expr2.Property.Name)
            {
                return false;
            } 

            if (expr1.Instance.ExpressionKind != expr2.Instance.ExpressionKind) 
            { 
                return false;
            } 

            if (expr1.Instance.ExpressionKind == DbExpressionKind.Property)
            {
                return AreMatching((DbPropertyExpression)expr1.Instance, (DbPropertyExpression)expr2.Instance, expr1BindingVariableName, expr2BindingVariableName); 
            }
 
            DbVariableReferenceExpression instance1 =  (DbVariableReferenceExpression)expr1.Instance; 
            DbVariableReferenceExpression instance2 =  (DbVariableReferenceExpression)expr2.Instance;
 
            return (String.Equals(instance1.VariableName, expr1BindingVariableName, StringComparison.Ordinal)
                && String.Equals(instance2.VariableName, expr2BindingVariableName, StringComparison.Ordinal));
        }
 
        /// 
        /// Helper method for  
        /// Creates a  over the given inputBinding that projects out the given flattenedProperties. 
        /// and updates the flattenedProperties to be over the newly created project.
        ///  
        /// 
        /// 
        /// An  over the newly created 
        private DbExpressionBinding CapWithProject(DbExpressionBinding inputBinding, IList flattenedProperties) 
        {
            List> projectColumns = new List>(flattenedProperties.Count); 
 
            //List of all the columnNames used in the projection.
            Dictionary columnNames = new Dictionary(flattenedProperties.Count); 

            foreach (DbPropertyExpression pe in flattenedProperties)
            {
                //There may be conflicting property names, thus we may need to rename. 
                string name = pe.Property.Name;
                int i; 
                if (columnNames.TryGetValue(name, out i)) 
                {
                    string newName; 
                    do
                    {
                        ++i;
                        newName = name + i.ToString(System.Globalization.CultureInfo.InvariantCulture); 
                    } while (columnNames.ContainsKey(newName));
 
                    columnNames[name] = i; 
                    name = newName;
                } 

                // Add this column name to list of known names so that there are no subsequent
                // collisions
                columnNames[name] = 0; 
                projectColumns.Add(new KeyValuePair(name, pe));
            } 
 
            //Build the project
            DbExpression rowExpr = CommandTree.CreateNewRowExpression(projectColumns); 
            DbProjectExpression projectExpression = CommandTree.CreateProjectExpression(inputBinding, rowExpr);

            //Create the new inputBinding
            DbExpressionBinding resultBinding = this.CommandTree.CreateExpressionBinding(projectExpression); 

            //Create the list of flattenedProperties over the new project 
            flattenedProperties.Clear(); 
            RowType rowExprType = (RowType)rowExpr.ResultType.EdmType;
 
            foreach (KeyValuePair column in projectColumns)
            {
                EdmProperty prop = rowExprType.Properties[column.Key];
                flattenedProperties.Add( 
                    CommandTree.CreatePropertyExpression(
                    prop, 
                    CommandTree.CreateVariableReferenceExpression(resultBinding.VariableName, resultBinding.VariableType))); 
            }
            return resultBinding; 
        }

        #endregion
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK