Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataEntity / System / Data / SqlClient / SqlGen / Sql8ExpressionRewriter.cs / 2 / 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

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- FormatException.cs
- QilGeneratorEnv.cs
- DataBoundControlAdapter.cs
- ChannelTokenTypeConverter.cs
- DataGridViewBand.cs
- XdrBuilder.cs
- Publisher.cs
- TemplateContent.cs
- WebPartsPersonalization.cs
- FilterElement.cs
- IOException.cs
- SessionEndedEventArgs.cs
- WebPartTracker.cs
- PolyBezierSegment.cs
- Query.cs
- _AutoWebProxyScriptEngine.cs
- PrintController.cs
- FtpWebRequest.cs
- DataGridViewControlCollection.cs
- MembershipUser.cs
- UnauthorizedWebPart.cs
- ConfigurationHelpers.cs
- SharedUtils.cs
- GroupQuery.cs
- MetadataSerializer.cs
- RenderData.cs
- FacetEnabledSchemaElement.cs
- DataTableNewRowEvent.cs
- AssociationEndMember.cs
- SuppressMergeCheckAttribute.cs
- GridItem.cs
- Misc.cs
- XmlSchemaSequence.cs
- DrawingContext.cs
- FormViewModeEventArgs.cs
- querybuilder.cs
- StaticContext.cs
- LogWriteRestartAreaAsyncResult.cs
- DataGrid.cs
- RijndaelManagedTransform.cs
- DataKey.cs
- PrintingPermissionAttribute.cs
- ControlTemplate.cs
- UInt64Storage.cs
- DiscreteKeyFrames.cs
- ColorPalette.cs
- HtmlInputText.cs
- VideoDrawing.cs
- TemplatePropertyEntry.cs
- MappingModelBuildProvider.cs
- VersionedStreamOwner.cs
- ClientUtils.cs
- RegexWriter.cs
- ExpressionHelper.cs
- TextOptionsInternal.cs
- LineVisual.cs
- DataPagerCommandEventArgs.cs
- TableTextElementCollectionInternal.cs
- DataServiceHostFactory.cs
- FormViewPagerRow.cs
- RelationshipDetailsCollection.cs
- ReachSerializableProperties.cs
- FixedPageStructure.cs
- DbProviderFactoriesConfigurationHandler.cs
- StrokeNodeOperations.cs
- WebBrowserUriTypeConverter.cs
- RoleServiceManager.cs
- UserControlBuildProvider.cs
- AssemblyAssociatedContentFileAttribute.cs
- PipeStream.cs
- DataChangedEventManager.cs
- TypeBuilder.cs
- SizeValueSerializer.cs
- FixedPageStructure.cs
- InfoCardAsymmetricCrypto.cs
- SynchronizationValidator.cs
- GroupDescription.cs
- ServiceBuildProvider.cs
- EntityDesignerUtils.cs
- PersonalizationStateInfo.cs
- Int32RectValueSerializer.cs
- WebBaseEventKeyComparer.cs
- WindowsSysHeader.cs
- PagedDataSource.cs
- EncodingFallbackAwareXmlTextWriter.cs
- XComponentModel.cs
- DependencyObject.cs
- Int32Animation.cs
- GridViewColumnCollection.cs
- ButtonField.cs
- _AutoWebProxyScriptWrapper.cs
- FieldTemplateFactory.cs
- PathFigureCollection.cs
- PerfService.cs
- StringUtil.cs
- MediaSystem.cs
- OutOfMemoryException.cs
- invalidudtexception.cs
- DataReceivedEventArgs.cs
- BlockCollection.cs