DiscriminatorMap.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 / Map / ViewGeneration / DiscriminatorMap.cs / 1305376 / DiscriminatorMap.cs

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

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

namespace System.Data.Mapping.ViewGeneration 
{
    /// 
    /// Describes top-level query mapping view projection of the form:
    /// 
    /// SELECT VALUE CASE
    ///     WHEN Discriminator = DiscriminatorValue1 THEN EntityType1(...) 
    ///     WHEN Discriminator = DiscriminatorValue2 THEN EntityType2(...) 
    ///     ...
    /// 
    /// Supports optimizing queries to leverage user supplied discriminator values
    /// in TPH mappings rather than introducing our own. This avoids the need
    /// to introduce a CASE statement in the store.
    ///  
    internal class DiscriminatorMap
    { 
        ///  
        /// Expression retrieving discriminator value from projection input.
        ///  
        internal readonly DbPropertyExpression Discriminator;
        /// 
        /// Map from discriminator values to implied entity type.
        ///  
        internal readonly System.Collections.ObjectModel.ReadOnlyCollection> TypeMap;
        ///  
        /// Map from entity property to expression generating value for that property. Note that 
        /// the expression must be the same for all types in discriminator map.
        ///  
        internal readonly System.Collections.ObjectModel.ReadOnlyCollection> PropertyMap;
        /// 
        /// Map from entity relproperty to expression generating value for that property. Note that
        /// the expression must be the same for all types in discriminator map. 
        /// 
        internal readonly System.Collections.ObjectModel.ReadOnlyCollection> RelPropertyMap; 
 
        /// 
        /// EntitySet to which the map applies. 
        /// 
        internal readonly EntitySet EntitySet;

        private DiscriminatorMap(DbPropertyExpression discriminator, 
            List> typeMap,
            Dictionary propertyMap, 
            Dictionary relPropertyMap, 
            EntitySet entitySet)
        { 
            this.Discriminator = discriminator;
            this.TypeMap = typeMap.AsReadOnly();
            this.PropertyMap = propertyMap.ToList().AsReadOnly();
            this.RelPropertyMap = relPropertyMap.ToList().AsReadOnly(); 
            this.EntitySet = entitySet;
        } 
 
        /// 
        /// Determines whether the given query view matches the discriminator map pattern. 
        /// 
        internal static bool TryCreateDiscriminatorMap(EntitySet entitySet, DbExpression queryView, out DiscriminatorMap discriminatorMap)
        {
            discriminatorMap = null; 

            if (queryView.ExpressionKind != DbExpressionKind.Project) { return false; } 
            var project = (DbProjectExpression)queryView; 

            if (project.Projection.ExpressionKind != DbExpressionKind.Case) { return false; } 
            var caseExpression = (DbCaseExpression)project.Projection;
            if (project.Projection.ResultType.EdmType.BuiltInTypeKind != BuiltInTypeKind.EntityType) { return false; }

            // determine value domain by walking filter 
            if (project.Input.Expression.ExpressionKind != DbExpressionKind.Filter) { return false; }
            var filterExpression = (DbFilterExpression)project.Input.Expression; 
 
            HashSet discriminatorDomain = new HashSet();
            if (!ViewSimplifier.TryMatchDiscriminatorPredicate(filterExpression, (equalsExp, discriminatorValue) => discriminatorDomain.Add(discriminatorValue))) 
            {
                return false;
            }
 
            var typeMap = new List>();
            var propertyMap = new Dictionary(); 
            var relPropertyMap = new Dictionary(); 
            var typeToRelPropertyMap = new Dictionary>();
            DbPropertyExpression discriminator = null; 

            EdmProperty discriminatorProperty = null;
            for (int i = 0; i < caseExpression.When.Count; i++)
            { 
                var when = caseExpression.When[i];
                var then = caseExpression.Then[i]; 
 
                var projectionVariableName = project.Input.VariableName;
 
                DbPropertyExpression currentDiscriminator;
                object discriminatorValue;
                if (!ViewSimplifier.TryMatchPropertyEqualsValue(when, projectionVariableName, 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; } 
                discriminator = currentDiscriminator;
 
                // right hand side must be entity type constructor
                EntityType currentType;
                if (!TryMatchEntityTypeConstructor(then, propertyMap, relPropertyMap, typeToRelPropertyMap, out currentType)) { return false; }
 
                // remember type + discriminator value
                typeMap.Add(new KeyValuePair(discriminatorValue, currentType)); 
 
                // remove discriminator value from domain
                discriminatorDomain.Remove(discriminatorValue); 
            }

            // make sure only one member of discriminator domain remains...
            if (1 != discriminatorDomain.Count) { return false; } 

            // check default case 
            EntityType elseType; 
            if (null == caseExpression.Else ||
                !TryMatchEntityTypeConstructor(caseExpression.Else, propertyMap, relPropertyMap, typeToRelPropertyMap, out elseType)) { return false; } 
            typeMap.Add(new KeyValuePair(discriminatorDomain.Single(), elseType));

            // Account for cases where some type in the hierarchy specifies a rel-property, but another
            // type in the hierarchy does not 
            if (!CheckForMissingRelProperties(relPropertyMap, typeToRelPropertyMap))
            { 
                return false; 
            }
 
            // since the store may right-pad strings, ensure discriminator values are unique in their trimmed
            // form
            var discriminatorValues = typeMap.Select(map => map.Key);
            int uniqueValueCount = discriminatorValues.Distinct(TrailingSpaceComparer.Instance).Count(); 
            int valueCount = typeMap.Count;
            if (uniqueValueCount != valueCount) { return false; } 
 
            discriminatorMap = new DiscriminatorMap(discriminator, typeMap, propertyMap, relPropertyMap, entitySet);
            return true; 
        }

        private static bool CheckForMissingRelProperties(
            Dictionary relPropertyMap, 
            Dictionary> typeToRelPropertyMap)
        { 
            // Easily the lousiest implementation of this search. 
            // Check to see that for each relProperty that we see in the relPropertyMap
            // (presumably because some type constructor specified it), every type for 
            // which that rel-property is specified *must* also have specified it.
            // We don't need to check for equivalence here - because that's already been
            // checked
            foreach (Query.InternalTrees.RelProperty relProperty in relPropertyMap.Keys) 
            {
                foreach (KeyValuePair> kv in typeToRelPropertyMap) 
                { 
                    if (kv.Key.IsSubtypeOf(relProperty.FromEnd.TypeUsage.EdmType))
                    { 
                        if (!kv.Value.Contains(relProperty))
                        {
                            return false;
                        } 
                    }
                } 
            } 
            return true;
        } 

        private static bool TryMatchEntityTypeConstructor(DbExpression then,
            Dictionary propertyMap,
            Dictionary relPropertyMap, 
            Dictionary> typeToRelPropertyMap,
            out EntityType entityType) 
        { 
            if (then.ExpressionKind != DbExpressionKind.NewInstance)
            { 
                entityType = null;
                return false;
            }
            var constructor = (DbNewInstanceExpression)then; 
            entityType = (EntityType)constructor.ResultType.EdmType;
 
            // process arguments to constructor (must be aligned across all case statements) 
            Debug.Assert(entityType.Properties.Count == constructor.Arguments.Count, "invalid new instance");
            for (int j = 0; j < entityType.Properties.Count; j++) 
            {
                var property = entityType.Properties[j];
                var assignment = constructor.Arguments[j];
                DbExpression existingAssignment; 
                if (propertyMap.TryGetValue(property, out existingAssignment))
                { 
                    if (!ExpressionsCompatible(assignment, existingAssignment)) { return false; } 
                }
                else 
                {
                    propertyMap.Add(property, assignment);
                }
            } 

            // Now handle the rel properties 
            if (constructor.HasRelatedEntityReferences) 
            {
                List relPropertyList; 
                if (!typeToRelPropertyMap.TryGetValue(entityType, out relPropertyList))
                {
                    relPropertyList = new List();
                    typeToRelPropertyMap[entityType] = relPropertyList; 
                }
                foreach (DbRelatedEntityRef relatedRef in constructor.RelatedEntityReferences) 
                { 
                    Query.InternalTrees.RelProperty relProperty = new System.Data.Query.InternalTrees.RelProperty((RelationshipType)relatedRef.TargetEnd.DeclaringType,
                        relatedRef.SourceEnd, relatedRef.TargetEnd); 
                    DbExpression assignment = relatedRef.TargetEntityReference;
                    DbExpression existingAssignment;
                    if (relPropertyMap.TryGetValue(relProperty, out existingAssignment))
                    { 
                        if (!ExpressionsCompatible(assignment, existingAssignment)) { return false; }
                    } 
                    else 
                    {
                        relPropertyMap.Add(relProperty, assignment); 
                    }
                    relPropertyList.Add(relProperty);
                }
            } 
            return true;
        } 
 
        /// 
        /// Utility method determining whether two expressions appearing within the same scope 
        /// are equivalent. May return false negatives, but no false positives. In other words,
        ///
        ///     x != y --> !ExpressionsCompatible(x, y)
        /// 
        /// but does not guarantee
        /// 
        ///     x == y --> ExpressionsCompatible(x, y) 
        /// 
        private static bool ExpressionsCompatible(DbExpression x, DbExpression y) 
        {
            if (x.ExpressionKind != y.ExpressionKind) { return false; }
            switch (x.ExpressionKind)
            { 
                case DbExpressionKind.Property:
                    { 
                        var prop1 = (DbPropertyExpression)x; 
                        var prop2 = (DbPropertyExpression)y;
                        return prop1.Property == prop2.Property && 
                            ExpressionsCompatible(prop1.Instance, prop2.Instance);
                    }
                case DbExpressionKind.VariableReference:
                    return ((DbVariableReferenceExpression)x).VariableName == 
                        ((DbVariableReferenceExpression)y).VariableName;
                case DbExpressionKind.NewInstance: 
                    { 
                        var newX = (DbNewInstanceExpression)x;
                        var newY = (DbNewInstanceExpression)y; 
                        if (!newX.ResultType.EdmType.EdmEquals(newY.ResultType.EdmType)) { return false; }
                        for (int i = 0; i < newX.Arguments.Count; i++)
                        {
                            if (!ExpressionsCompatible(newX.Arguments[i], newY.Arguments[i])) 
                            {
                                return false; 
                            } 
                        }
                        return true; 
                    }
                case DbExpressionKind.Ref:
                    {
                        DbRefExpression refX = (DbRefExpression)x; 
                        DbRefExpression refY = (DbRefExpression)y;
                        return (refX.EntitySet.EdmEquals(refY.EntitySet) && 
                            ExpressionsCompatible(refX.Argument, refY.Argument)); 
                    }
                default: 
                    // here come the false negatives...
                    return false;
            }
        } 
    }
} 

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