Exemplo n.º 1
0
        internal MaterializeAtom(ResponseInfo responseInfo, QueryComponents queryComponents, ProjectionPlan plan, IODataResponseMessage responseMessage, ODataPayloadKind payloadKind)
        {
            Type type;

            this.responseInfo            = responseInfo;
            this.elementType             = queryComponents.LastSegmentType;
            this.MergeOptionValue        = responseInfo.MergeOption;
            this.expectingPrimitiveValue = PrimitiveType.IsKnownNullableType(this.elementType);
            Type materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.MaxProtocolVersion, out type);

            this.materializer = ODataMaterializer.CreateMaterializerForMessage(responseMessage, responseInfo, materializerType, queryComponents, plan, payloadKind);
        }
Exemplo n.º 2
0
        internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable <ODataEntry> entries, Type elementType)
        {
            Type type;

            this.responseInfo            = responseInfo;
            this.elementType             = elementType;
            this.MergeOptionValue        = responseInfo.MergeOption;
            this.expectingPrimitiveValue = PrimitiveType.IsKnownNullableType(elementType);
            Type            expectedType    = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.MaxProtocolVersion, out type);
            QueryComponents queryComponents = new QueryComponents(null, Util.DataServiceVersionEmpty, elementType, null, null);

            this.materializer = new ODataEntriesEntityMaterializer(entries, responseInfo, queryComponents, expectedType, null);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Returns the primitive property value.
        /// </summary>
        /// <param name="propertyValue">Value of the property.</param>
        /// <param name="propertyType">Type of the property.</param>
        /// <returns>Returns the value of the primitive property.</returns>
        private static object ConvertPrimitiveValueToRecognizedODataType(object propertyValue, Type propertyType)
        {
            Debug.Assert(PrimitiveType.IsKnownNullableType(propertyType), "GetPrimitiveValue must be called only for primitive types");
            Debug.Assert(propertyValue == null || PrimitiveType.IsKnownType(propertyValue.GetType()), "GetPrimitiveValue method must be called for primitive values only");

            if (propertyValue == null)
            {
                return(null);
            }

            PrimitiveType primitiveType;

            PrimitiveType.TryGetPrimitiveType(propertyType, out primitiveType);
            Debug.Assert(primitiveType != null, "must be a known primitive type");

            // Do the conversion for types that are not supported by ODataLib e.g. char[], char, etc
            if (propertyType == typeof(Char) ||
                propertyType == typeof(Char[]) ||
                propertyType == typeof(Type) ||
                propertyType == typeof(Uri) ||
                propertyType == typeof(Xml.Linq.XDocument) ||
                propertyType == typeof(Xml.Linq.XElement))
            {
                return(primitiveType.TypeConverter.ToString(propertyValue));
            }
#if !ASTORIA_LIGHT && !PORTABLELIB
            else if (propertyType.FullName == "System.Data.Linq.Binary")
            {
                // For System.Data.Linq.Binary, it is a delay loaded type. Hence checking it based on name.
                // PrimitiveType.IsKnownType checks for binary type based on name and assembly. Hence just
                // checking name here is sufficient, since any other type with the same name, but in different
                // assembly will return false for PrimitiveType.IsKnownNullableType.
                // Since ODataLib does not understand binary type, we need to convert the value to byte[].
                return(((BinaryTypeConverter)primitiveType.TypeConverter).ToArray(propertyValue));
            }
#endif
            else if (primitiveType.EdmTypeName == null)
            {
                // case StorageType.DateTimeOffset:
                // case StorageType.TimeSpan:
                // case StorageType.UInt16:
                // case StorageType.UInt32:
                // case StorageType.UInt64:
                // don't support reverse mappings for these types in this version
                // allows us to add real server support in the future without a
                // "breaking change" in the future client
                throw new NotSupportedException(Strings.ALinq_CantCastToUnsupportedPrimitive(propertyType.Name));
            }

            return(propertyValue);
        }
Exemplo n.º 4
0
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="responseInfo">originating context</param>
        /// <param name="entries">entries that needs to be materialized.</param>
        /// <param name="elementType">result type.</param>
        /// <param name="format">The format of the response being materialized from.</param>
        internal MaterializeAtom(ResponseInfo responseInfo, IEnumerable <ODataEntry> entries, Type elementType, ODataFormat format)
        {
            this.responseInfo            = responseInfo;
            this.elementType             = elementType;
            this.expectingPrimitiveValue = PrimitiveType.IsKnownNullableType(elementType);

            Type                     implementationType;
            Type                     materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.Model, out implementationType);
            QueryComponents          qc      = new QueryComponents(null, Util.DataServiceVersionEmpty, elementType, null, null);
            ODataMaterializerContext context = new ODataMaterializerContext(responseInfo);
            EntityTrackingAdapter    entityTrackingAdapter = new EntityTrackingAdapter(responseInfo.EntityTracker, responseInfo.MergeOption, responseInfo.Model, responseInfo.Context);

            this.materializer = new ODataEntriesEntityMaterializer(entries, context, entityTrackingAdapter, qc, materializerType, null, format);
        }
Exemplo n.º 5
0
 internal static void ValidateCollection(Type collectionItemType, object propertyValue, string propertyName)
 {
     if (!PrimitiveType.IsKnownNullableType(collectionItemType) && (collectionItemType.GetInterfaces().SingleOrDefault <Type>(t => (t == typeof(IEnumerable))) != null))
     {
         throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.ClientType_CollectionOfCollectionNotSupported);
     }
     if (propertyValue == null)
     {
         if (propertyName != null)
         {
             throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Collection_NullCollectionNotSupported(propertyName));
         }
         throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Collection_NullNonPropertyCollectionNotSupported(collectionItemType));
     }
 }
Exemplo n.º 6
0
 internal static bool IsCLRTypeCollection(Type type, DataServiceProtocolVersion maxProtocolVersion)
 {
     if (!PrimitiveType.IsKnownNullableType(type))
     {
         Type implementationType = ClientTypeUtil.GetImplementationType(type, typeof(ICollection <>));
         if ((implementationType != null) && !ClientTypeUtil.TypeIsEntity(implementationType.GetGenericArguments()[0], maxProtocolVersion))
         {
             if (maxProtocolVersion <= DataServiceProtocolVersion.V2)
             {
                 throw new InvalidOperationException(System.Data.Services.Client.Strings.WebUtil_CollectionTypeNotSupportedInV2OrBelow(type.FullName));
             }
             return(true);
         }
     }
     return(false);
 }
Exemplo n.º 7
0
        internal static void ValidatePrimitiveCollectionItem(object itemValue, string propertyName, Type collectionItemType)
        {
            Type type = itemValue.GetType();

            if (!PrimitiveType.IsKnownNullableType(type))
            {
                throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.Collection_ComplexTypesInCollectionOfPrimitiveTypesNotAllowed);
            }
            if (!collectionItemType.IsAssignableFrom(type))
            {
                if (propertyName != null)
                {
                    throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.WebUtil_TypeMismatchInCollection(propertyName));
                }
                throw System.Data.Services.Client.Error.InvalidOperation(System.Data.Services.Client.Strings.WebUtil_TypeMismatchInNonPropertyCollection(collectionItemType));
            }
        }
Exemplo n.º 8
0
            /// <summary>
            /// Visits a member access expression in non-entity projections, validating that
            /// it's correct and recording the path visit to include in a projection if necessary.
            /// </summary>
            /// <param name="m">Expression to visit.</param>
            /// <returns>The same expression.</returns>
            /// <remarks>
            /// The projection analyzer runs after funcletization, so a member expression
            /// rather than a constant expression implies that this is correlated to
            /// a parameter, by dotting through the argument in valid cases, and possibly
            /// more complex cases in others like new DSC(p.Orders)*.Foo* &lt;- .Foo is invalid.
            /// </remarks>
            internal override Expression VisitMemberAccess(MemberExpression m)
            {
                Debug.Assert(m != null, "m != null");
                Type expressionType = m.Expression.Type;

                this.leafExpressionIsMemberAccess = true;

                // if primitive or nullable primitive, allow member access... i.e. calling Value on nullable<int>
                if (PrimitiveType.IsKnownNullableType(expressionType))
                {
                    this.leafExpressionIsMemberAccess = false;
                    return(base.VisitMemberAccess(m));
                }

                // Only allowed to project entities, also it is ok to do client side projections on complex types.
                // Details on the fix for the Dev11 bug 350541 "Inconsistency between Count() method call and Count property projection on clr type collections":
                //     Relax check to only throw if IsCollectionProducingExpression returns true.
                //     This enables client side projections (for example "Count") on Clr type collections, like ReadOnlyCollection (which is used in spatial types), ICollection, IList, etc.
                //     We already allow client side method calls (like Linq extension method "Count()") on clr type collections, so it makes client side projections consistent.
                //     Note: it will still throw for List<T> (because IsCollectionProducingExpression returns true for List<T>),
                //           however this is consistent with how we handle MethodCallExpression on clr type collections
                //           and changing IsCollectionProducingExpression seems risky at this point as it's used in a lot of places.
                if (IsCollectionProducingExpression(m.Expression))
                {
                    throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString()));
                }

                PropertyInfo pi;
                Expression   boundTarget;

                if (ResourceBinder.PatternRules.MatchNonPrivateReadableProperty(m, out pi, out boundTarget))
                {
                    Expression e = base.VisitMemberAccess(m);
                    if (ClientTypeUtil.TypeOrElementTypeIsEntity(expressionType))
                    {
                        Type convertedType;
                        ResourceBinder.StripTo <Expression>(m.Expression, out convertedType);
                        box.AppendPropertyToPath(pi, convertedType, this.context);
                        this.leafExpressionIsMemberAccess = false;
                    }

                    return(e);
                }

                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString()));
            }
Exemplo n.º 9
0
        internal override Expression VisitMemberAccess(MemberExpression m)
        {
            ExpressionAnnotation annotation;
            Expression           expression = m.Expression;

            if (PrimitiveType.IsKnownNullableType(expression.Type))
            {
                return(base.VisitMemberAccess(m));
            }
            Expression key = this.Visit(expression);

            if (this.annotations.TryGetValue(key, out annotation))
            {
                return(this.RebindMemberAccess(m, annotation));
            }
            return(Expression.MakeMemberAccess(key, m.Member));
        }
Exemplo n.º 10
0
        /// <summary>
        /// Get or create a client EDM type instance.
        /// </summary>
        /// <param name="type">type to wrap</param>
        /// <returns>client type</returns>
        private EdmTypeCacheValue GetOrCreateEdmTypeInternal(Type type)
        {
            Debug.Assert(type != null, "type != null");

            EdmTypeCacheValue cachedEdmType;

            lock (this.clrToEdmTypeCache)
            {
                this.clrToEdmTypeCache.TryGetValue(type, out cachedEdmType);
            }

            if (cachedEdmType == null)
            {
                if (PrimitiveType.IsKnownNullableType(type))
                {
                    cachedEdmType = this.GetOrCreateEdmTypeInternal(null /*baseType*/, type, ClientTypeUtil.EmptyPropertyInfoArray, false /*isEntity*/, false /*hasProperties*/);
                }
                else
                {
                    PropertyInfo[] keyProperties;
                    bool           hasProperties;
                    Type[]         hierarchy = ClientEdmModel.GetTypeHierarchy(type, out keyProperties, out hasProperties);

                    Debug.Assert(keyProperties == null || keyProperties.Length == 0 || keyProperties.All(p => p.DeclaringType == keyProperties[0].DeclaringType), "All key properties must be declared on the same type.");

                    bool isEntity = keyProperties != null;
                    keyProperties = keyProperties ?? ClientTypeUtil.EmptyPropertyInfoArray;
                    foreach (Type t in hierarchy)
                    {
                        // Pass in the full list of key properties for the most base type to be added there.  We only allow key properties to be
                        // declared on the same type.
                        IEdmStructuredType edmBaseType = cachedEdmType == null ? null : cachedEdmType.EdmType as IEdmStructuredType;
                        cachedEdmType = this.GetOrCreateEdmTypeInternal(edmBaseType, t, keyProperties, isEntity, t == type ? hasProperties : (bool?)null);

                        // Pass in an empty PropertyInfo array on subsequent derived types.
                        keyProperties = ClientTypeUtil.EmptyPropertyInfoArray;
                    }
                }
            }

            Debug.Assert(cachedEdmType != null, "cachedEdmType != null");
            return(cachedEdmType);
        }
Exemplo n.º 11
0
#pragma warning restore 649
#endif

        #endregion Private fields

        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="responseInfo">originating context</param>
        /// <param name="queryComponents">Query components (projection, expected type)</param>
        /// <param name="plan">Projection plan (if compiled in an earlier query).</param>
        /// <param name="responseMessage">responseMessage</param>
        /// <param name="payloadKind">The kind of the payload to materialize.</param>
        internal MaterializeAtom(
            ResponseInfo responseInfo,
            QueryComponents queryComponents,
            ProjectionPlan plan,
            IODataResponseMessage responseMessage,
            ODataPayloadKind payloadKind)
        {
            Debug.Assert(queryComponents != null, "queryComponents != null");

            this.responseInfo            = responseInfo;
            this.elementType             = queryComponents.LastSegmentType;
            this.expectingPrimitiveValue = PrimitiveType.IsKnownNullableType(elementType);

            Debug.Assert(responseMessage != null, "Response message is null! Did you mean to use Materializer.ResultsWrapper/EmptyResults?");
            this.responseMessage = responseMessage;

            Type implementationType;
            Type materializerType = GetTypeForMaterializer(this.expectingPrimitiveValue, this.elementType, responseInfo.Model, out implementationType);

            this.materializer = ODataMaterializer.CreateMaterializerForMessage(responseMessage, responseInfo, materializerType, queryComponents, plan, payloadKind);
        }
Exemplo n.º 12
0
        internal void OnPrimitiveOrComplexCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            object obj2;
            string str;
            Type   type;

            Util.CheckArgumentNull <object>(sender, "sender");
            Util.CheckArgumentNull <NotifyCollectionChangedEventArgs>(e, "e");
            this.bindingGraph.GetPrimitiveOrComplexCollectionInfo(sender, out obj2, out str, out type);
            if (!PrimitiveType.IsKnownNullableType(type))
            {
                switch (e.Action)
                {
                case NotifyCollectionChangedAction.Add:
                    this.OnAddToComplexTypeCollection(sender, e.NewItems);
                    goto Label_00BC;

                case NotifyCollectionChangedAction.Remove:
                    this.OnRemoveFromComplexTypeCollection(sender, e.OldItems);
                    goto Label_00BC;

                case NotifyCollectionChangedAction.Replace:
                    this.OnRemoveFromComplexTypeCollection(sender, e.OldItems);
                    this.OnAddToComplexTypeCollection(sender, e.NewItems);
                    goto Label_00BC;

                case NotifyCollectionChangedAction.Move:
                    goto Label_00BC;

                case NotifyCollectionChangedAction.Reset:
                    this.bindingGraph.RemoveCollection(sender);
                    goto Label_00BC;
                }
                throw new InvalidOperationException(Strings.DataBinding_CollectionChangedUnknownActionCollection(e.Action, sender.GetType()));
            }
Label_00BC:
            this.HandleUpdateEntity(obj2, str, sender);
        }
Exemplo n.º 13
0
        /// <summary>
        /// Checks if the collection is valid. Throws if the collection is not valid.
        /// </summary>
        /// <param name="collectionItemType">The type of the collection item. Can not be null.</param>
        /// <param name="propertyValue">Collection instance to be validated.</param>
        /// <param name="propertyName">The name of the property being serialized (for exception messages). Can be null if the type is not a property.</param>
        internal static void ValidateCollection(Type collectionItemType, object propertyValue, string propertyName)
        {
            Debug.Assert(collectionItemType != null, "collectionItemType != null");

            // nested collections are not supported. Need to exclude primitve types - e.g. string implements IEnumerable<char>
            if (!PrimitiveType.IsKnownNullableType(collectionItemType) &&
                collectionItemType.GetInterfaces().SingleOrDefault(t => t == typeof(IEnumerable)) != null)
            {
                throw Error.InvalidOperation(Strings.ClientType_CollectionOfCollectionNotSupported);
            }

            if (propertyValue == null)
            {
                if (propertyName != null)
                {
                    throw Error.InvalidOperation(Strings.Collection_NullCollectionNotSupported(propertyName));
                }
                else
                {
                    throw Error.InvalidOperation(Strings.Collection_NullNonPropertyCollectionNotSupported(collectionItemType));
                }
            }
        }
Exemplo n.º 14
0
        /// <summary>
        /// Checks if the provided type is a collection type (i.e. it implements ICollection and the collection item type is not an entity).
        /// </summary>
        /// <param name="type">Type being checked.</param>
        /// <param name="model">The client model.</param>
        /// <returns>True if the CLR type is a collection compatible type. False otherwise.</returns>
        internal static bool IsCLRTypeCollection(Type type, ClientEdmModel model)
        {
            // char[] and byte[] implements ICollection<> but we should not threat them as collections since they are primitive types for us.
            if (!PrimitiveType.IsKnownNullableType(type))
            {
                Type collectionType = ClientTypeUtil.GetImplementationType(type, typeof(ICollection <>));
                if (collectionType != null)
                {
                    // collectionType is ICollection so we know that the first generic parameter
                    // is the collection item type
                    if (!ClientTypeUtil.TypeIsEntity(collectionType.GetGenericArguments()[0], model))
                    {
                        if (model.MaxProtocolVersion <= DataServiceProtocolVersion.V2)
                        {
                            throw new InvalidOperationException(Strings.WebUtil_CollectionTypeNotSupportedInV2OrBelow(type.FullName));
                        }

                        return(true);
                    }
                }
            }

            return(false);
        }
Exemplo n.º 15
0
        internal override Expression VisitUnary(UnaryExpression u)
        {
            UnaryExpression expression = (UnaryExpression)base.VisitUnary(u);
            Expression      rewritten  = expression;

            this.RecordRewrite(u, rewritten);
            if ((expression.NodeType != ExpressionType.Convert) && (expression.NodeType != ExpressionType.TypeAs))
            {
                return(rewritten);
            }
            if (!expression.Type.IsAssignableFrom(expression.Operand.Type))
            {
                return(rewritten);
            }
            if ((PrimitiveType.IsKnownNullableType(expression.Operand.Type) || PrimitiveType.IsKnownNullableType(expression.Type)) && !(expression.Operand.Type == expression.Type))
            {
                return(rewritten);
            }
            if (ClientTypeUtil.TypeOrElementTypeIsEntity(expression.Operand.Type) && ProjectionAnalyzer.IsCollectionProducingExpression(expression.Operand))
            {
                return(rewritten);
            }
            return(expression.Operand);
        }
Exemplo n.º 16
0
        private EdmTypeCacheValue GetOrCreateEdmTypeInternal(IEdmStructuredType edmBaseType, Type type, PropertyInfo[] keyProperties, bool isEntity, bool?hasProperties)
        {
            EdmTypeCacheValue value2;
            Action <MetadataProviderEdmEntityType>  action3 = null;
            Action <MetadataProviderEdmComplexType> action4 = null;

            lock (this.clrToEdmTypeCache)
            {
                this.clrToEdmTypeCache.TryGetValue(type, out value2);
            }
            if (value2 == null)
            {
                if (PrimitiveType.IsKnownNullableType(type))
                {
                    PrimitiveType type3;
                    PrimitiveType.TryGetPrimitiveType(type, out type3);
                    value2 = new EdmTypeCacheValue(type3.CreateEdmPrimitiveType(), hasProperties);
                }
                else
                {
                    Type type2;
                    if (((type2 = ClientTypeUtil.GetImplementationType(type, typeof(ICollection <>))) != null) && (ClientTypeUtil.GetImplementationType(type, typeof(IDictionary <,>)) == null))
                    {
                        Type type4 = type2.GetGenericArguments()[0];
                        value2 = new EdmTypeCacheValue(new EdmCollectionType(this.GetOrCreateEdmTypeInternal(type4).EdmType.ToEdmTypeReference(ClientTypeUtil.CanAssignNull(type4))), hasProperties);
                    }
                    else if (isEntity)
                    {
                        if (action3 == null)
                        {
                            action3 = delegate(MetadataProviderEdmEntityType entityType) {
                                List <IEdmProperty>           list  = new List <IEdmProperty>();
                                List <IEdmStructuralProperty> list1 = new List <IEdmStructuralProperty>();
                                using (IEnumerator <PropertyInfo> enumerator = (from p in ClientTypeUtil.GetPropertiesOnType(type, edmBaseType != null)
                                                                                orderby p.Name
                                                                                select p).GetEnumerator())
                                {
                                    Func <PropertyInfo, bool> predicate = null;
                                    while (enumerator.MoveNext())
                                    {
                                        PropertyInfo property = enumerator.Current;
                                        IEdmProperty item     = this.CreateEdmProperty(entityType, property);
                                        list.Add(item);
                                        if (edmBaseType == null)
                                        {
                                            if (predicate == null)
                                            {
                                                predicate = k => (k.DeclaringType == type) && (k.Name == property.Name);
                                            }
                                            if (keyProperties.Any <PropertyInfo>(predicate))
                                            {
                                                list1.Add((IEdmStructuralProperty)item);
                                            }
                                        }
                                    }
                                }
                                foreach (IEdmProperty property2 in list)
                                {
                                    entityType.AddProperty(property2);
                                }
                                entityType.AddKeys(list1);
                            };
                        }
                        Action <MetadataProviderEdmEntityType> propertyLoadAction = action3;
                        value2 = new EdmTypeCacheValue(new MetadataProviderEdmEntityType(CommonUtil.GetModelTypeNamespace(type), CommonUtil.GetModelTypeName(type), (IEdmEntityType)edmBaseType, type.IsAbstract(), false, propertyLoadAction), hasProperties);
                    }
                    else
                    {
                        if (action4 == null)
                        {
                            action4 = delegate(MetadataProviderEdmComplexType complexType) {
                                List <IEdmProperty> list = new List <IEdmProperty>();
                                foreach (PropertyInfo info in from p in ClientTypeUtil.GetPropertiesOnType(type, edmBaseType != null)
                                         orderby p.Name
                                         select p)
                                {
                                    IEdmProperty item = this.CreateEdmProperty(complexType, info);
                                    list.Add(item);
                                }
                                foreach (IEdmProperty property2 in list)
                                {
                                    complexType.AddProperty(property2);
                                }
                            };
                        }
                        Action <MetadataProviderEdmComplexType> action2 = action4;
                        value2 = new EdmTypeCacheValue(new MetadataProviderEdmComplexType(CommonUtil.GetModelTypeNamespace(type), CommonUtil.GetModelTypeName(type), (IEdmComplexType)edmBaseType, type.IsAbstract(), action2), hasProperties);
                    }
                }
                IEdmType             edmType = value2.EdmType;
                ClientTypeAnnotation orCreateClientTypeAnnotation = this.GetOrCreateClientTypeAnnotation(edmType, type);
                edmType.SetClientTypeAnnotation(orCreateClientTypeAnnotation);
                if ((edmType.TypeKind == EdmTypeKind.Entity) || (edmType.TypeKind == EdmTypeKind.Complex))
                {
                    IEdmStructuredType edmStructuredType = edmType as IEdmStructuredType;
                    this.SetMimeTypeForProperties(edmStructuredType);
                }
                lock (this.clrToEdmTypeCache)
                {
                    EdmTypeCacheValue value3;
                    if (this.clrToEdmTypeCache.TryGetValue(type, out value3))
                    {
                        return(value3);
                    }
                    this.clrToEdmTypeCache.Add(type, value2);
                }
            }
            return(value2);
        }
Exemplo n.º 17
0
        private EdmTypeCacheValue GetOrCreateEdmTypeInternal(IEdmStructuredType edmBaseType, Type type, PropertyInfo[] keyProperties, bool isEntity, bool?hasProperties)
        {
            Debug.Assert(type != null, "type != null");
            Debug.Assert(keyProperties != null, "keyProperties != null");

            EdmTypeCacheValue cachedEdmType;

            lock (this.clrToEdmTypeCache)
            {
                this.clrToEdmTypeCache.TryGetValue(type, out cachedEdmType);
            }

            if (cachedEdmType == null)
            {
                Type collectionType;
                if (PrimitiveType.IsKnownNullableType(type))
                {
                    PrimitiveType primitiveType;
                    PrimitiveType.TryGetPrimitiveType(type, out primitiveType);
                    Debug.Assert(primitiveType != null, "primitiveType != null");
                    cachedEdmType = new EdmTypeCacheValue(primitiveType.CreateEdmPrimitiveType(), hasProperties);
                }
                else if ((collectionType = ClientTypeUtil.GetImplementationType(type, typeof(ICollection <>))) != null && ClientTypeUtil.GetImplementationType(type, typeof(IDictionary <,>)) == null)
                {
                    // Collection Type
                    Type     elementType = collectionType.GetGenericArguments()[0];
                    IEdmType itemType    = this.GetOrCreateEdmTypeInternal(elementType).EdmType;

                    // Note that while collection of a collection is not allowed, we cannot throw here since it's a breaking change.
                    // We will throw during SaveChanges() and we have unit test validating the error case.
                    Debug.Assert(
                        itemType.TypeKind == EdmTypeKind.Entity || itemType.TypeKind == EdmTypeKind.Complex || itemType.TypeKind == EdmTypeKind.Primitive || itemType.TypeKind == EdmTypeKind.Collection,
                        "itemType.TypeKind == EdmTypeKind.Entity || itemType.TypeKind == EdmTypeKind.Complex || itemType.TypeKind == EdmTypeKind.Primitive || itemType.TypeKind == EdmTypeKind.Collection");

                    cachedEdmType = new EdmTypeCacheValue(new EdmCollectionType(itemType.ToEdmTypeReference(ClientTypeUtil.CanAssignNull(elementType))), hasProperties);
                }
                else
                {
                    if (isEntity)
                    {
                        Action <EdmEntityTypeWithDelayLoadedProperties> delayLoadEntityProperties = (entityType) =>
                        {
                            // Create properties without modifying the entityType.
                            // This will leave entityType intact in case of an exception during loading.
                            List <IEdmProperty>           loadedProperties    = new List <IEdmProperty>();
                            List <IEdmStructuralProperty> loadedKeyProperties = new List <IEdmStructuralProperty>();
                            foreach (PropertyInfo property in ClientTypeUtil.GetPropertiesOnType(type, /*declaredOnly*/ edmBaseType != null).OrderBy(p => p.Name))
                            {
                                IEdmProperty edmProperty = this.CreateEdmProperty((EdmStructuredType)entityType, property);
                                loadedProperties.Add(edmProperty);

                                if (edmBaseType == null && keyProperties.Any(k => k.DeclaringType == type && k.Name == property.Name))
                                {
                                    Debug.Assert(edmProperty.PropertyKind == EdmPropertyKind.Structural, "edmProperty.PropertyKind == EdmPropertyKind.Structural");
                                    Debug.Assert(edmProperty.Type.TypeKind() == EdmTypeKind.Primitive, "edmProperty.Type.TypeKind() == EdmTypeKind.Primitive");
                                    loadedKeyProperties.Add((IEdmStructuralProperty)edmProperty);
                                }
                            }

                            // Now add properties to the entityType.
                            foreach (IEdmProperty property in loadedProperties)
                            {
                                entityType.AddProperty(property);
                            }

                            entityType.AddKeys(loadedKeyProperties);
                        };

                        // Creating an entity type
                        Debug.Assert(edmBaseType == null || edmBaseType.TypeKind == EdmTypeKind.Entity, "baseType == null || baseType.TypeKind == EdmTypeKind.Entity");
                        cachedEdmType = new EdmTypeCacheValue(
                            new EdmEntityTypeWithDelayLoadedProperties(CommonUtil.GetModelTypeNamespace(type), CommonUtil.GetModelTypeName(type), (IEdmEntityType)edmBaseType, c.PlatformHelper.IsAbstract(type), false /*isOpen*/, delayLoadEntityProperties),
                            hasProperties);
                    }
                    else
                    {
                        Action <EdmComplexTypeWithDelayLoadedProperties> delayLoadComplexProperties = (complexType) =>
                        {
                            // Create properties without modifying the complexType.
                            // This will leave complexType intact in case of an exception during loading.
                            List <IEdmProperty> loadedProperties = new List <IEdmProperty>();
                            foreach (PropertyInfo property in ClientTypeUtil.GetPropertiesOnType(type, /*declaredOnly*/ edmBaseType != null).OrderBy(p => p.Name))
                            {
                                IEdmProperty edmProperty = this.CreateEdmProperty(complexType, property);
                                loadedProperties.Add(edmProperty);
                            }

                            // Now add properties to the complexType.
                            foreach (IEdmProperty property in loadedProperties)
                            {
                                complexType.AddProperty(property);
                            }
                        };

                        // Creating a complex type
                        Debug.Assert(edmBaseType == null || edmBaseType.TypeKind == EdmTypeKind.Complex, "baseType == null || baseType.TypeKind == EdmTypeKind.Complex");
                        cachedEdmType = new EdmTypeCacheValue(
                            new EdmComplexTypeWithDelayLoadedProperties(CommonUtil.GetModelTypeNamespace(type), CommonUtil.GetModelTypeName(type), (IEdmComplexType)edmBaseType, c.PlatformHelper.IsAbstract(type), delayLoadComplexProperties),
                            hasProperties);
                    }
                }

                Debug.Assert(cachedEdmType != null, "cachedEdmType != null");

                IEdmType             edmType = cachedEdmType.EdmType;
                ClientTypeAnnotation clientTypeAnnotation = this.GetOrCreateClientTypeAnnotation(edmType, type);
                this.SetClientTypeAnnotation(edmType, clientTypeAnnotation);

                if (edmType.TypeKind == EdmTypeKind.Entity || edmType.TypeKind == EdmTypeKind.Complex)
                {
                    IEdmStructuredType edmStructuredType = edmType as IEdmStructuredType;
                    Debug.Assert(edmStructuredType != null, "edmStructuredType != null");
                    this.SetMimeTypeForProperties(edmStructuredType);
                }

                // Need to cache the type before loading the properties so we don't stack overflow because
                // loading the property can trigger calls to GetOrCreateEdmType on the same type.
                lock (this.clrToEdmTypeCache)
                {
                    EdmTypeCacheValue existing;
                    if (this.clrToEdmTypeCache.TryGetValue(type, out existing))
                    {
                        cachedEdmType = existing;
                    }
                    else
                    {
                        this.clrToEdmTypeCache.Add(type, cachedEdmType);
                    }
                }
            }

            return(cachedEdmType);
        }