DiscriminatorMap.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / DiscriminatorMap.cs / 1 / 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; }

            EdmProperty discriminatorProperty = null; 

            // determine value domain by walking filter 
            HashSet discriminatorDomain = new HashSet(); 
            if (project.Input.Expression.ExpressionKind != DbExpressionKind.Filter) { return false; }
            var filterExpression = (DbFilterExpression)project.Input.Expression; 

            // check each assignment in predicate
            foreach (var term in FlattenOr(filterExpression.Predicate))
            { 
                DbPropertyExpression currentDiscriminator;
                object discriminatorValue; 
                if (!TryMatchPropertyEqualsValue(term, filterExpression.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; }

                discriminatorDomain.Add(discriminatorValue); 
            }
 
            var typeMap = new List>(); 
            var propertyMap = new Dictionary();
            var relPropertyMap = new Dictionary(); 
            var typeToRelPropertyMap = new Dictionary>();
            DbPropertyExpression discriminator = 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 (!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 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; 
        }
 
        private static IEnumerable FlattenOr(DbExpression expression)
        {
            if (expression.ExpressionKind == DbExpressionKind.Or)
            { 
                return CommandTreeUtils.FlattenAssociativeExpression(expression);
            } 
            return new DbExpression[] { expression }; 
        }
 
        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.
//---------------------------------------------------------------------- 
// 
//      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; }

            EdmProperty discriminatorProperty = null; 

            // determine value domain by walking filter 
            HashSet discriminatorDomain = new HashSet(); 
            if (project.Input.Expression.ExpressionKind != DbExpressionKind.Filter) { return false; }
            var filterExpression = (DbFilterExpression)project.Input.Expression; 

            // check each assignment in predicate
            foreach (var term in FlattenOr(filterExpression.Predicate))
            { 
                DbPropertyExpression currentDiscriminator;
                object discriminatorValue; 
                if (!TryMatchPropertyEqualsValue(term, filterExpression.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; }

                discriminatorDomain.Add(discriminatorValue); 
            }
 
            var typeMap = new List>(); 
            var propertyMap = new Dictionary();
            var relPropertyMap = new Dictionary(); 
            var typeToRelPropertyMap = new Dictionary>();
            DbPropertyExpression discriminator = 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 (!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 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; 
        }
 
        private static IEnumerable FlattenOr(DbExpression expression)
        {
            if (expression.ExpressionKind == DbExpressionKind.Or)
            { 
                return CommandTreeUtils.FlattenAssociativeExpression(expression);
            } 
            return new DbExpression[] { expression }; 
        }
 
        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