ViewSimplifier.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Common / CommandTrees / Internal / ViewSimplifier.cs / 1305376 / ViewSimplifier.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....], [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Data.Common.CommandTrees; 
using System.Collections.Generic;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Data.Common.Utils; 
using System.Linq;
using System.Globalization; 
using System.Data.Common.CommandTrees.ExpressionBuilder; 
using System.Data.Common.CommandTrees.Internal;
 
namespace System.Data.Common.CommandTrees.Internal
{

    ///  
    /// Utility class that walks a mapping view and returns a simplified expression with projection
    /// nodes collapsed. Specifically recognizes the following common pattern in mapping views: 
    /// 
    ///     outerProject(outerBinding(innerProject(innerBinding, innerNew)), outerProjection)
    /// 
    /// Recognizes simple disciminator patterns of the form:
    ///
    ///     select
    ///         case when Disc = value1 then value Type1(...) 
    ///         case when Disc = value2 then value Type2(...)
    ///         ... 
    /// 
    /// Recognizes redundant case statement of the form:
    /// 
    ///     select
    ///         case when (case when Predicate1 then true else false) ...
    ///
    ///  
    internal class ViewSimplifier
    { 
        internal static DbQueryCommandTree SimplifyView(EntitySetBase extent, DbQueryCommandTree view) 
        {
            ViewSimplifier vs = new ViewSimplifier(view.MetadataWorkspace, extent); 
            view = vs.Simplify(view);
            return view;
        }
 
        private readonly MetadataWorkspace metadata;
        private readonly EntitySetBase extent; 
 
        private ViewSimplifier(MetadataWorkspace mws, EntitySetBase viewTarget)
        { 
            this.metadata = mws;
            this.extent = viewTarget;
        }
 
        private DbQueryCommandTree Simplify(DbQueryCommandTree view)
        { 
            var simplifier = PatternMatchRuleProcessor.Create( 
                // determines if an expression is of the form outerProject(outerProjection(innerProject(innerNew)))
                PatternMatchRule.Create(Pattern_CollapseNestedProjection, ViewSimplifier.CollapseNestedProjection), 

                // A case statement can potentially be simplified
                PatternMatchRule.Create(Pattern_Case, ViewSimplifier.SimplifyCaseStatement),
 
                // Nested TPH discriminator pattern can be converted to the expected TPH discriminator pattern
                PatternMatchRule.Create(Pattern_NestedTphDiscriminator, ViewSimplifier.SimplifyNestedTphDiscriminator), 
 
                // Entity constructors may be augmented with FK-based related entity refs
                PatternMatchRule.Create(Pattern_EntityConstructor, this.AddFkRelatedEntityRefs) 
            );

            DbExpression queryExpression = view.Query;
            queryExpression = simplifier(queryExpression); 

            view = DbQueryCommandTree.FromValidExpression(view.MetadataWorkspace, view.DataSpace, queryExpression); 
            return view; 
        }
 
        #region Navigation simplification support by adding FK-based related entity refs

        private static readonly Func Pattern_EntityConstructor =
            Patterns.MatchProject( 
                Patterns.AnyExpression,
                Patterns.And( 
                    Patterns.MatchEntityType, 
                    Patterns.Or
                    ( 
                        Patterns.MatchNewInstance(),
                        Patterns.MatchCase(Patterns.AnyExpressions, Patterns.MatchForAll(Patterns.MatchNewInstance()), Patterns.MatchNewInstance())
                    )
                ) 
            );
 
        private bool doNotProcess; 

        private DbExpression AddFkRelatedEntityRefs(DbExpression viewConstructor) 
        {
            // If the extent being simplified is not a C-Space entity set, or if it has already
            // been processed by the simplifier, then keep the original expression by returning
            // null. 
            //
            if (this.doNotProcess) 
            { 
                return null;
            } 

            if(this.extent.BuiltInTypeKind != BuiltInTypeKind.EntitySet ||
               this.extent.EntityContainer.DataSpace != DataSpace.CSpace)
            { 
                this.doNotProcess = true;
                return null; 
            } 

            // Get a reference to the entity set being simplified, and find all the foreign key 
            // (foreign key) associations for which the association set references that entity set,
            // with either association end.
            //
            EntitySet targetSet = (EntitySet)this.extent; 
            var relSets =
                targetSet.EntityContainer.BaseEntitySets 
                .Where(es => es.BuiltInTypeKind == BuiltInTypeKind.AssociationSet) 
                .Cast()
                .Where(assocSet => 
                          assocSet.ElementType.IsForeignKey &&
                          assocSet.AssociationSetEnds.Any(se => se.EntitySet == targetSet)
                       )
                .ToList(); 

            // If no foreign key association sets that reference the entity set are present, then 
            // no further processing is necessary, because FK-based related entity references cannot 
            // be computed and added to the entities constructed for the entity set.
            if (relSets.Count == 0) 
            {
                this.doNotProcess = true;
                return null;
            } 

            // For every relationship set that references this entity set, the relationship type and 
            // foreign key constraint are used to determine if the entity set is the dependent set. 
            // If it is the dependent set, then it is possible to augment the view definition with a
            // related entity ref that represents the navigation of the relationship set's relationship 
            // from the dependent end (this entity set) to the the principal end (the entity set that
            // is referenced by the other association set end of the relationship set).
            //
            var principalSetsAndDependentTypes = new HashSet>(); 
            foreach (AssociationSet relSet in relSets)
            { 
                // Retrieve the single referential constraint from the foreign key association, and 
                // use it to determine whether the association set end that represents the dependent
                // end of the association references this entity set. 
                //
                var fkConstraint = relSet.ElementType.ReferentialConstraints[0];
                var dependentSetEnd = relSet.AssociationSetEnds[fkConstraint.ToRole.Name];
 
                if (dependentSetEnd.EntitySet == targetSet)
                { 
                    EntityType requiredSourceNavType = (EntityType)TypeHelpers.GetEdmType(dependentSetEnd.CorrespondingAssociationEndMember.TypeUsage).ElementType; 
                    var principalSetEnd = relSet.AssociationSetEnds[fkConstraint.FromRole.Name];
 
                    // Record the entity type that an element of this dependent entity set must have in order
                    // to be a valid navigation source for the relationship set's relationship, along with the
                    // association set end for the destination (principal) end of the navigation and the FK
                    // constraint that is associated with the relationship type. This information may be used 
                    // later to construct a related entity ref for any entity constructor expression in the view
                    // that produces an entity of the required source type or a subtype. 
                    // 
                    principalSetsAndDependentTypes.Add(Tuple.Create(requiredSourceNavType, principalSetEnd, fkConstraint));
                } 
            }

            // If no foreign key association sets that use the entity set as the dependent set are present,
            // then no further processing is possible, since FK-based related entity refs can only be added 
            // to the view definition for navigations from the dependent end of the relationship to the principal.
            // 
            if (principalSetsAndDependentTypes.Count == 0) 
            {
                this.doNotProcess = true; 
                return null;
            }

            // This rule supports a view that is capped with a projection of the form 
            // (input).Project(x => new Entity())
            // or 
            // (input).Project(x => CASE WHEN (condition1) THEN new Entity1() ELSE WHEN (condition2) THEN new Entity2()... ELSE new EntityN()) 
            // where every new instance expression Entity1()...EntityN() constructs an entity of a type
            // that is compatible with the entity set's element type. 
            // Here, the list of all DbNewInstanceExpressions contained in the projection is remembered,
            // along with any CASE statement conditions, if present. These expressions will be updated
            // if necessary and used to build a new capping projection if any of the entity constructors
            // are augmented with FK-based related entity references. 
            //
            DbProjectExpression entityProject = (DbProjectExpression)viewConstructor; 
            List constructors = new List(); 
            List conditions = null;
            if (entityProject.Projection.ExpressionKind == DbExpressionKind.Case) 
            {
                // If the projection is a DbCaseExpression, then every result must be a DbNewInstanceExpression
                DbCaseExpression discriminatedConstructor = (DbCaseExpression)entityProject.Projection;
                conditions = new List(discriminatedConstructor.When.Count); 
                for (int idx = 0; idx < discriminatedConstructor.When.Count; idx++)
                { 
                    conditions.Add(discriminatedConstructor.When[idx]); 
                    constructors.Add((DbNewInstanceExpression)discriminatedConstructor.Then[idx]);
                } 
                constructors.Add((DbNewInstanceExpression)discriminatedConstructor.Else);
            }
            else
            { 
                // Otherwise, the projection must be a single DbNewInstanceExpression
                constructors.Add((DbNewInstanceExpression)entityProject.Projection); 
            } 

            bool rebuildView = false; 
            for (int idx = 0; idx < constructors.Count; idx++)
            {
                DbNewInstanceExpression entityConstructor = constructors[idx];
                EntityType constructedEntityType = TypeHelpers.GetEdmType(entityConstructor.ResultType); 

                List relatedRefs = 
                    principalSetsAndDependentTypes 
                    .Where(psdt => constructedEntityType == psdt.Item1 || constructedEntityType.IsSubtypeOf(psdt.Item1))
                    .Select(psdt => RelatedEntityRefFromAssociationSetEnd(constructedEntityType, entityConstructor, psdt.Item2, psdt.Item3)).ToList(); 

                if (relatedRefs.Count > 0)
                {
                    if (entityConstructor.HasRelatedEntityReferences) 
                    {
                        relatedRefs = entityConstructor.RelatedEntityReferences.Concat(relatedRefs).ToList(); 
                    } 

                    entityConstructor = DbExpressionBuilder.CreateNewEntityWithRelationshipsExpression(constructedEntityType, entityConstructor.Arguments, relatedRefs); 
                    constructors[idx] = entityConstructor;
                    rebuildView = true;
                }
            } 

            // Default to returning null to indicate that this rule did not produce a modified expression 
            // 
            DbExpression result = null;
            if (rebuildView) 
            {
                // rebuildView is true, so entity constructing DbNewInstanceExpression(s) were encountered
                // and updated with additional related entity refs. The DbProjectExpression that caps the
                // view definition therefore needs to be rebuilt and returned as the result of this rule. 
                //
                if (conditions != null) 
                { 
                    // The original view definition projection was a DbCaseExpression.
                    // The new expression is also a DbCaseExpression that uses the conditions from the 
                    // original expression together with the updated result expressions to produce the
                    // new capping projection.
                    //
                    List whens = new List(conditions.Count); 
                    List thens = new List(conditions.Count);
                    for (int idx = 0; idx < conditions.Count; idx++) 
                    { 
                        whens.Add(conditions[idx]);
                        thens.Add(constructors[idx]); 
                    }

                    result = entityProject.Input.Project(DbExpressionBuilder.Case(whens, thens, constructors[conditions.Count]));
                } 
                else
                { 
                    // Otherwise, the capping projection consists entirely of the updated DbNewInstanceExpression. 
                    //
                    result = entityProject.Input.Project(constructors[0]); 
                }
            }

            // Regardless of whether or not the view was updated, this rule should not be applied again during rule processing 
            this.doNotProcess = true;
            return result; 
        } 

        private static DbRelatedEntityRef RelatedEntityRefFromAssociationSetEnd(EntityType constructedEntityType, DbNewInstanceExpression entityConstructor, AssociationSetEnd principalSetEnd, ReferentialConstraint fkConstraint) 
        {
            EntityType principalEntityType = (EntityType)TypeHelpers.GetEdmType(fkConstraint.FromRole.TypeUsage).ElementType;
            IList principalKeyValues = null;
 
            // Create Entity Property/DbExpression value pairs from the entity constructor DbExpression,
            // then join these with the principal/dependent property pairs from the FK constraint 
            // to produce principal property name/DbExpression value pairs from which to create the principal ref. 
            //
            // Ideally the code would be as below, but anonymous types break asmmeta: 
            //var keyPropAndValue =
            //    from pv in constructedEntityType.Properties.Select((p, idx) => new { DependentProperty = p, Value = entityConstructor.Arguments[idx] })
            //    join ft in fkConstraint.FromProperties.Select((fp, idx) => new { PrincipalProperty = fp, DependentProperty = fkConstraint.ToProperties[idx] })
            //    on pv.DependentProperty equals ft.DependentProperty 
            //    select new { PrincipalProperty = ft.PrincipalProperty.Name, Value = pv.Value };
            // 
            var keyPropAndValue = 
                from pv in constructedEntityType.Properties.Select((p, idx) => Tuple.Create(p, entityConstructor.Arguments[idx])) // new { DependentProperty = p, Value = entityConstructor.Arguments[idx] })
                join ft in fkConstraint.FromProperties.Select((fp, idx) => Tuple.Create(fp, fkConstraint.ToProperties[idx])) //new { PrincipalProperty = fp, DependentProperty = fkConstraint.ToProperties[idx] }) 
                on pv.Item1 equals ft.Item2 //pv.DependentProperty equals ft.DependentProperty
                select Tuple.Create(ft.Item1.Name, pv.Item2); // new { PrincipalProperty = ft.PrincipalProperty.Name, Value = pv.Value };

            // If there is only a single property in the principal's key, then there is no ordering concern. 
            // Otherwise, create a dictionary of principal key property name to DbExpression value so that
            // when used as the arguments to the ref expression, the dependent property values - used here 
            // as principal key property values - are in the correct order, which is the same order as the 
            // key members themselves.
            // 
            if (fkConstraint.FromProperties.Count == 1)
            {
                var singleKeyNameAndValue = keyPropAndValue.Single();
                Debug.Assert(singleKeyNameAndValue.Item1 == fkConstraint.FromProperties[0].Name, "Unexpected single key property name"); 
                principalKeyValues = new[] { singleKeyNameAndValue.Item2 };
            } 
            else 
            {
                var keyValueMap = keyPropAndValue.ToDictionary(pav => pav.Item1, pav => pav.Item2, StringComparer.Ordinal); 
                principalKeyValues = principalEntityType.KeyMemberNames.Select(memberName => keyValueMap[memberName]).ToList();
            }

            // Create the ref to the principal entity based on the (now correctly ordered) key value expressions. 
            //
            DbRefExpression principalRef = principalSetEnd.EntitySet.CreateRef(principalEntityType, principalKeyValues); 
            DbRelatedEntityRef result = DbExpressionBuilder.CreateRelatedEntityRef(fkConstraint.ToRole, fkConstraint.FromRole, principalRef); 

            return result; 
        }

        #endregion
 
        #region Nested TPH Discriminator simplification
 
        ///  
        /// Matches the nested TPH discriminator pattern produced by view generation
        ///  
        private static readonly Func Pattern_NestedTphDiscriminator =
            Patterns.MatchProject(
                Patterns.MatchFilter(
                    Patterns.MatchProject( 
                        Patterns.MatchFilter(
                            Patterns.AnyExpression, 
                            Patterns.Or( 
                                Patterns.MatchKind(DbExpressionKind.Equals),
                                Patterns.MatchKind(DbExpressionKind.Or) 
                            )
                        ),
                        Patterns.And(
                            Patterns.MatchRowType, 
                            Patterns.MatchNewInstance(
                                Patterns.MatchForAll( 
                                    Patterns.Or( 
                                        Patterns.And(
                                            Patterns.MatchNewInstance(), 
                                            Patterns.MatchComplexType
                                        ),
                                        Patterns.MatchKind(DbExpressionKind.Property),
                                        Patterns.MatchKind(DbExpressionKind.Case) 
                                    )
                                 ) 
                            ) 
                        )
                    ), 
                    Patterns.Or(
                        Patterns.MatchKind(DbExpressionKind.Property),
                        Patterns.MatchKind(DbExpressionKind.Or)
                    ) 
                ),
                Patterns.And( 
                    Patterns.MatchEntityType, 
                    Patterns.MatchCase(
                        Patterns.MatchForAll(Patterns.MatchKind(DbExpressionKind.Property)), 
                        Patterns.MatchForAll(Patterns.MatchKind(DbExpressionKind.NewInstance)),
                        Patterns.MatchKind(DbExpressionKind.NewInstance)
                    )
                ) 
            );
 
        ///  
        /// Converts the DbExpression equivalent of:
        /// 
        /// SELECT CASE
        ///     WHEN a._from0 THEN SUBTYPE1()
        ///     ...
        ///     WHEN a._from[n-2] THEN SUBTYPE_n-1() 
        ///     ELSE SUBTYPE_n
        /// FROM 
        ///     SELECT 
        ///         b.C1..., b.Cn
        ///         CASE WHEN b.Discriminator = SUBTYPE1_Value THEN true ELSE false AS _from0 
        ///         ...
        ///         CASE WHEN b.Discriminator = SUBTYPE_n_Value THEN true ELSE false AS _from[n-1]
        ///     FROM TSet AS b
        ///     WHERE b.Discriminator = SUBTYPE1_Value... OR x.Discriminator = SUBTYPE_n_Value 
        /// AS a
        /// WHERE a._from0... OR a._from[n-1] 
        /// 
        /// into the DbExpression equivalent of the following, which is matched as a TPH discriminator
        /// by the  class and so allows a  
        /// to be produced for the view, which would not otherwise be possible. Note that C1 through Cn
        /// are only allowed to be scalars or complex type constructors based on direct property references
        /// to the store entity set's scalar properties.
        /// 
        /// SELECT CASE
        ///     WHEN y.Discriminator = SUBTTYPE1_Value THEN SUBTYPE1() 
        ///     ... 
        ///     WHEN y.Discriminator = SUBTYPE_n-1_Value THEN SUBTYPE_n-1()
        ///     ELSE SUBTYPE_n() 
        /// FROM
        ///     SELECT x.C1..., x.Cn, Discriminator FROM TSet AS x
        ///     WHERE x.Discriminator = SUBTYPE1_Value... OR x.Discriminator = SUBTYPE_n_Value
        /// AS y 
        ///
        ///  
        private static DbExpression SimplifyNestedTphDiscriminator(DbExpression expression) 
        {
            DbProjectExpression entityProjection = (DbProjectExpression)expression; 
            DbFilterExpression booleanColumnFilter = (DbFilterExpression)entityProjection.Input.Expression;
            DbProjectExpression rowProjection = (DbProjectExpression)booleanColumnFilter.Input.Expression;
            DbFilterExpression discriminatorFilter = (DbFilterExpression)rowProjection.Input.Expression;
 
            List predicates = FlattenOr(booleanColumnFilter.Predicate).ToList();
            List propertyPredicates = 
                predicates.OfType() 
                .Where(px => px.Instance.ExpressionKind == DbExpressionKind.VariableReference &&
                             ((DbVariableReferenceExpression)px.Instance).VariableName == booleanColumnFilter.Input.VariableName).ToList(); 
            if (predicates.Count != propertyPredicates.Count)
            {
                return null;
            } 

            List predicateColumnNames = propertyPredicates.Select(px => px.Property.Name).ToList(); 
 
            Dictionary discriminatorPredicates = new Dictionary();
            if (!TypeSemantics.IsEntityType(discriminatorFilter.Input.VariableType) || 
                !TryMatchDiscriminatorPredicate(discriminatorFilter, (compEx, discValue) => discriminatorPredicates.Add(discValue, compEx)))
            {
                return null;
            } 

            EdmProperty discriminatorProp = (EdmProperty)((DbPropertyExpression)((DbComparisonExpression)discriminatorPredicates.First().Value).Left).Property; 
            DbNewInstanceExpression rowConstructor = (DbNewInstanceExpression)rowProjection.Projection; 
            RowType resultRow = TypeHelpers.GetEdmType(rowConstructor.ResultType);
            Dictionary inputPredicateMap = new Dictionary(); 
            Dictionary selectorPredicateMap = new Dictionary();
            Dictionary columnValues = new Dictionary(rowConstructor.Arguments.Count);
            for (int idx = 0; idx < rowConstructor.Arguments.Count; idx++)
            { 
                string propName = resultRow.Properties[idx].Name;
                DbExpression columnVal = rowConstructor.Arguments[idx]; 
                if (predicateColumnNames.Contains(propName)) 
                {
                    if(columnVal.ExpressionKind != DbExpressionKind.Case) 
                    {
                        return null;
                    }
                    DbCaseExpression casePredicate = (DbCaseExpression)columnVal; 
                    if(casePredicate.When.Count != 1 ||
                       !TypeSemantics.IsBooleanType(casePredicate.Then[0].ResultType) || !TypeSemantics.IsBooleanType(casePredicate.Else.ResultType) || 
                        casePredicate.Then[0].ExpressionKind != DbExpressionKind.Constant || casePredicate.Else.ExpressionKind != DbExpressionKind.Constant || 
                        (bool)((DbConstantExpression)casePredicate.Then[0]).Value != true || (bool)((DbConstantExpression)casePredicate.Else).Value != false)
                    { 
                        return null;
                    }

                    DbPropertyExpression comparedProp; 
                    object constValue;
                    if(!TryMatchPropertyEqualsValue(casePredicate.When[0], rowProjection.Input.VariableName, out comparedProp, out constValue) || 
                       comparedProp.Property != discriminatorProp || 
                       !discriminatorPredicates.ContainsKey(constValue))
                    { 
                        return null;
                    }

                    inputPredicateMap.Add(propName, discriminatorPredicates[constValue]); 
                    selectorPredicateMap.Add(propName, (DbComparisonExpression)casePredicate.When[0]);
                } 
                else 
                {
                    columnValues.Add(propName, columnVal); 
                }
            }

            // Build a new discriminator-based filter that only includes the same rows allowed by the higher '_from0' column-based filter 
            DbExpression newDiscriminatorPredicate = Helpers.BuildBalancedTreeInPlace(new List(inputPredicateMap.Values), (left, right) => DbExpressionBuilder.Or(left, right));
            discriminatorFilter = discriminatorFilter.Input.Filter(newDiscriminatorPredicate); 
 
            DbCaseExpression entitySelector = (DbCaseExpression)entityProjection.Projection;
            List newWhens = new List(entitySelector.When.Count); 
            List newThens = new List(entitySelector.Then.Count);

            for (int idx = 0; idx < entitySelector.When.Count; idx++)
            { 
                DbPropertyExpression propWhen = (DbPropertyExpression)entitySelector.When[idx];
                DbNewInstanceExpression entityThen = (DbNewInstanceExpression)entitySelector.Then[idx]; 
 
                DbComparisonExpression discriminatorWhen;
                if (!selectorPredicateMap.TryGetValue(propWhen.Property.Name, out discriminatorWhen)) 
                {
                    return null;
                }
                newWhens.Add(discriminatorWhen); 

                DbExpression inputBoundEntityConstructor = ValueSubstituter.Substitute(entityThen, entityProjection.Input.VariableName, columnValues); 
                newThens.Add(inputBoundEntityConstructor); 
            }
 
            DbExpression newElse = ValueSubstituter.Substitute(entitySelector.Else, entityProjection.Input.VariableName, columnValues);
            DbCaseExpression newEntitySelector = DbExpressionBuilder.Case(newWhens, newThens, newElse);

            DbExpression result = discriminatorFilter.BindAs(rowProjection.Input.VariableName).Project(newEntitySelector); 
            return result;
        } 
 
        private class ValueSubstituter : DefaultExpressionVisitor
        { 
            internal static DbExpression Substitute(DbExpression original, string referencedVariable, Dictionary propertyValues)
            {
                Debug.Assert(original != null, "Original expression cannot be null");
                ValueSubstituter visitor = new ValueSubstituter(referencedVariable, propertyValues); 
                return visitor.VisitExpression(original);
            } 
 
            private readonly string variableName;
            private readonly Dictionary replacements; 

            private ValueSubstituter(string varName, Dictionary replValues)
            {
                Debug.Assert(varName != null, "Variable name cannot be null"); 
                Debug.Assert(replValues != null, "Replacement values cannot be null");
 
                this.variableName = varName; 
                this.replacements = replValues;
            } 

            public override DbExpression Visit(DbPropertyExpression expression)
            {
                DbExpression result = null; 

                DbExpression replacementValue; 
                if (expression.Instance.ExpressionKind == DbExpressionKind.VariableReference && 
                    (((DbVariableReferenceExpression)expression.Instance).VariableName == this.variableName) &&
                    this.replacements.TryGetValue(expression.Property.Name, out replacementValue)) 
                {
                    result = replacementValue;
                }
                else 
                {
                    result = base.Visit(expression); 
                } 
                return result;
            } 
        }

        #endregion
 
        #region Case Statement Simplification
 
        ///  
        /// Matches any Case expression
        ///  
        private static readonly Func Pattern_Case = Patterns.MatchKind(DbExpressionKind.Case);

        private static DbExpression SimplifyCaseStatement(DbExpression expression)
        { 
            DbCaseExpression caseExpression = (DbCaseExpression)expression;
 
            // try simplifying predicates 
            bool predicateSimplified = false;
            List rewrittenPredicates = new List(caseExpression.When.Count); 
            foreach (var when in caseExpression.When)
            {
                DbExpression simplifiedPredicate;
                if (TrySimplifyPredicate(when, out simplifiedPredicate)) 
                {
                    rewrittenPredicates.Add(simplifiedPredicate); 
                    predicateSimplified = true; 
                }
                else 
                {
                    rewrittenPredicates.Add(when);
                }
            } 

            if (!predicateSimplified) { return null; } 
 
            caseExpression = DbExpressionBuilder.Case(rewrittenPredicates, caseExpression.Then, caseExpression.Else);
            return caseExpression; 
        }

        private static bool TrySimplifyPredicate(DbExpression predicate, out DbExpression simplified)
        { 
            simplified = null;
            if (predicate.ExpressionKind != DbExpressionKind.Case) { return false; } 
            var caseExpression = (DbCaseExpression)predicate; 
            if (caseExpression.Then.Count != 1 && caseExpression.Then[0].ExpressionKind == DbExpressionKind.Constant)
            { 
                return false;
            }
            var then = (DbConstantExpression)caseExpression.Then[0];
            if (!true.Equals(then.Value)) { return false; } 
            if (caseExpression.Else != null)
            { 
                if (caseExpression.Else.ExpressionKind != DbExpressionKind.Constant) { return false; } 
                var when = (DbConstantExpression)caseExpression.Else;
                if (!false.Equals(when.Value)) { return false; } 
            }
            simplified = caseExpression.When[0];
            return true;
        } 

        #endregion 
 
        #region Nested Projection Collapsing
 
        /// 
        /// Determines if an expression is of the form outerProject(outerProjection(innerProject(innerNew)))
        /// 
        private static readonly Func Pattern_CollapseNestedProjection = 
            Patterns.MatchProject(
                Patterns.MatchProject( 
                    Patterns.AnyExpression, 
                    Patterns.MatchKind(DbExpressionKind.NewInstance)
                 ), 
                 Patterns.AnyExpression
            );

        ///  
        /// Collapses outerProject(outerProjection(innerProject(innerNew)))
        ///  
        private static DbExpression CollapseNestedProjection(DbExpression expression) 
        {
            DbProjectExpression outerProject = (DbProjectExpression)expression; 
            DbExpression outerProjection = outerProject.Projection;
            DbProjectExpression innerProject = (DbProjectExpression)outerProject.Input.Expression;
            DbNewInstanceExpression innerNew = (DbNewInstanceExpression)innerProject.Projection;
 
            // get membername -> expression bindings for the inner select so that we know how map property
            // references to the inner projection 
            Dictionary bindings = new Dictionary(innerNew.Arguments.Count); 
            TypeUsage innerResultTypeUsage = innerNew.ResultType;
            RowType innerResultType = (RowType)innerResultTypeUsage.EdmType; 

            for (int ordinal = 0; ordinal < innerResultType.Members.Count; ordinal++)
            {
                bindings[innerResultType.Members[ordinal].Name] = innerNew.Arguments[ordinal]; 
            }
 
            // initialize an expression visitor that knows how to map arguments to the outer projection 
            // to the inner projection source
            ProjectionCollapser collapser = new ProjectionCollapser(bindings, outerProject.Input); 

            // replace all property references to the inner projection
            var replacementOuterProjection = collapser.CollapseProjection(outerProjection);
 
            // make sure the collapsing was successful; if not, give up on simplification
            if (collapser.IsDoomed) { return null; } 
 
            // set replacement value so that the expression replacer infrastructure can substitute
            // the collapsed projection in the expression tree 
            // continue collapsing projection until the pattern no longer matches
            DbProjectExpression replacementOuterProject = innerProject.Input.Project(replacementOuterProjection);
            return replacementOuterProject;
        } 

        ///  
        /// This expression visitor supports collapsing a nested projection matching the pattern described above. 
        ///
        /// For instance: 
        ///
        ///     select T.a as x, T.b as y, true as z from (select E.a as x, E.b as y from Extent E)
        ///
        /// resolves to: 
        ///
        ///     select E.a, E.b, true as z from Extent E 
        /// 
        /// In general,
        /// 
        ///     outerProject(
        ///         outerBinding(
        ///             innerProject(innerBinding, innerNew)
        ///         ), 
        ///         outerNew)
        /// 
        /// resolves to: 
        ///
        ///     replacementOuterProject( 
        ///         innerBinding,
        ///         replacementOuterNew)
        ///
        /// The outer projection is bound to the inner input source (outerBinding -> innerBinding) and 
        /// the outer new instance expression has its properties remapped to the inner new instance
        /// expression member expressions. 
        /// 
        /// This replacer is used to simplify argument value in a new instance expression OuterNew
        /// from an expression of the form: 
        ///
        ///      outerProject(outerBinding(innerProject(innerBinding, innerNew)), outerProjection)
        ///
        /// The replacer collapses the outer project terms to point at the innerNew expression. 
        /// Where possible, VarRef_outer.Property_outer is collapsed to VarRef_inner.Property.
        ///  
        private class ProjectionCollapser : DefaultExpressionVisitor 
        {
            // the replacer context keeps track of member bindings for var refs and the expression 
            // binding for the outer projection being remapped
            private Dictionary m_varRefMemberBindings;
            private DbExpressionBinding m_outerBinding;
            private bool m_doomed; 
            internal ProjectionCollapser(Dictionary varRefMemberBindings,
                DbExpressionBinding outerBinding) 
                : base() 
            {
                m_varRefMemberBindings = varRefMemberBindings; 
                m_outerBinding = outerBinding;
            }

            // Visit and identify the Property(VarRef "Outer binding") pattern, 
            // remapping the property to the appropriate inner projection member
            internal DbExpression CollapseProjection(DbExpression expression) 
            { 
                return this.VisitExpression(expression);
            } 

            public override DbExpression Visit(DbPropertyExpression property)
            {
                // check for a property of the outer projection binding (that can be remapped) 
                if (property.Instance.ExpressionKind == DbExpressionKind.VariableReference &&
                        IsOuterBindingVarRef((DbVariableReferenceExpression)property.Instance)) 
                { 
                    return m_varRefMemberBindings[property.Property.Name];
                } 
                return base.Visit(property);
            }

            public override DbExpression Visit(DbVariableReferenceExpression varRef) 
            {
                // if we encounter an unsubstitutued var ref, give up... 
                if (IsOuterBindingVarRef(varRef)) 
                {
                    m_doomed = true; 
                }
                return base.Visit(varRef);
            }
 
            /// 
            /// Heuristic check to make sure the var ref is the one we're supposed to be replacing. 
            ///  
            private bool IsOuterBindingVarRef(DbVariableReferenceExpression varRef)
            { 
                return varRef.VariableName == m_outerBinding.VariableName;
            }

            ///  
            /// Returns a value indicating that the transformation has failed.
            ///  
            internal bool IsDoomed 
            {
                get { return m_doomed; } 
            }
        }

        #endregion 

        #region Utility Methods 
 
        internal static IEnumerable FlattenOr(DbExpression expression)
        { 
            return Helpers.GetLeafNodes(expression,
                exp => (exp.ExpressionKind != DbExpressionKind.Or),
                exp => { DbOrExpression orExp = (DbOrExpression)exp; return new[] { orExp.Left, orExp.Right }; });
        } 

        internal static bool TryMatchDiscriminatorPredicate(DbFilterExpression filter, Action onMatchedComparison) 
        { 
            EdmProperty discriminatorProperty = null;
 
            // check each assignment in predicate
            foreach (var term in FlattenOr(filter.Predicate))
            {
                DbPropertyExpression currentDiscriminator; 
                object discriminatorValue;
                if (!TryMatchPropertyEqualsValue(term, filter.Input.VariableName, out currentDiscriminator, out discriminatorValue)) 
                { 
                    return false;
                } 

                // must be the same discriminator in every case
                if (null == discriminatorProperty)
                { 
                    discriminatorProperty = (EdmProperty)currentDiscriminator.Property;
                } 
                else if (discriminatorProperty != currentDiscriminator.Property) 
                {
                    return false; 
                }

                onMatchedComparison((DbComparisonExpression)term, discriminatorValue);
            } 

            return true; 
        } 

        internal static bool TryMatchPropertyEqualsValue(DbExpression expression, string propertyVariable, out DbPropertyExpression property, out object value) 
        {
            property = null;
            value = null;
            // make sure when is of the form Discriminator = Constant 
            if (expression.ExpressionKind != DbExpressionKind.Equals) { return false; }
            var equals = (DbBinaryExpression)expression; 
            if (equals.Left.ExpressionKind != DbExpressionKind.Property) { return false; } 
            property = (DbPropertyExpression)equals.Left;
            if (!TryMatchConstant(equals.Right, out value)) { return false; } 

            // verify the property is a property of the input variable
            if (property.Instance.ExpressionKind != DbExpressionKind.VariableReference ||
                ((DbVariableReferenceExpression)property.Instance).VariableName != propertyVariable) { return false; } 

            return true; 
        } 

        private static bool TryMatchConstant(DbExpression expression, out object value) 
        {
            if (expression.ExpressionKind == DbExpressionKind.Constant)
            {
                value = ((DbConstantExpression)expression).Value; 
                return true;
            } 
            if (expression.ExpressionKind == DbExpressionKind.Cast && 
                expression.ResultType.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType)
            { 
                var castExpression = (DbCastExpression)expression;
                if (TryMatchConstant(castExpression.Argument, out value))
                {
                    // convert the value 
                    var primitiveType = (PrimitiveType)expression.ResultType.EdmType;
 
                    // constant literals have already been validated by view gen... 
                    value = Convert.ChangeType(value, primitiveType.ClrEquivalentType, CultureInfo.InvariantCulture);
                    return true; 
                }
            }
            value = null;
            return false; 
        }
 
        #endregion 

        /* 
        private abstract class DiscriminatorValueCollection
        {
            /// 
            /// Add a new discriminator value. Returns true if the value is unique. 
            /// 
            internal abstract bool TryAddDiscriminatorValue(object value); 
 
            /// 
            /// Associate a discriminator value with the given type. Returns true if the value is known. 
            /// 
            internal abstract bool TryAddDiscriminatorValueAssignment(object value, EntityType entityType);

            ///  
            ///
            ///  
            internal abstract bool TryAssignDefaultType(EntityType entityType); 

            private class TypedDiscriminatorValueCollection 
            {
                private readonly TryConvert _convert;
                private readonly Func, T> _generateUniqueDiscriminator;
                private readonly HashSet _discriminatorValues; 
                private readonly Dictionary _discriminatorAssignments;
 
 
            }
 
            private delegate bool TryConvert(object input, out T output);
        }
         * */
    } 
}

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