Beispiel #1
0
        /// <summary>
        /// Checks if the value of a primitive collection item is valid. Throws if it finds the value invalid.
        /// </summary>
        /// <param name="itemValue">The value of the collection item.</param>
        /// <param name="propertyName">The name of the collection property being serialized. Can be null.</param>
        /// <param name="collectionItemType">The type of the collection item as declared by the collection.</param>
        internal static void ValidatePrimitiveCollectionItem(object itemValue, string propertyName, Type collectionItemType)
        {
            Debug.Assert(itemValue != null, "itemValue != null. The ValidateDataServiceCollectionItem needs to be called first on each item.");
            Debug.Assert(collectionItemType != null, "collectionItemType != null");
            Debug.Assert(PrimitiveType.IsKnownNullableType(collectionItemType), "This method should only be called for primitive types.");

            Type itemValueType = itemValue.GetType();

            if (!PrimitiveType.IsKnownNullableType(itemValueType))
            {
                throw Error.InvalidOperation(Strings.Collection_ComplexTypesInCollectionOfPrimitiveTypesNotAllowed);
            }

            if (!collectionItemType.IsAssignableFrom(itemValueType))
            {
                if (propertyName != null)
                {
                    throw Error.InvalidOperation(Strings.WebUtil_TypeMismatchInCollection(propertyName));
                }
                else
                {
                    throw Error.InvalidOperation(Strings.WebUtil_TypeMismatchInNonPropertyCollection(collectionItemType));
                }
            }
        }
Beispiel #2
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>
        /// <param name="isDynamicProperty">Whether this collection property is a dynamic property</param>
        internal static void ValidateCollection(Type collectionItemType, object propertyValue, string propertyName, bool isDynamicProperty)
        {
            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)
                {
                    if (!isDynamicProperty)
                    {
                        throw Error.InvalidOperation(Strings.Collection_NullCollectionNotSupported(propertyName));
                    }
                }
                else
                {
                    throw Error.InvalidOperation(Strings.Collection_NullNonPropertyCollectionNotSupported(collectionItemType));
                }
            }
        }
Beispiel #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>
        internal 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(System.Xml.Linq.XDocument) ||
                propertyType == typeof(System.Xml.Linq.XElement))
            {
                return(primitiveType.TypeConverter.ToString(propertyValue));
            }
            else if (propertyType == typeof(DateTime))
            {
                return(PlatformHelper.ConvertDateTimeToDateTimeOffset((DateTime)propertyValue));
            }
#if !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);
        }
Beispiel #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 <ODataResource> 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.ODataVersionEmpty, 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);
        }
            /// <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);
                        this.builder.AppendPropertyToPath(pi, convertedType, this.context);
                        this.leafExpressionIsMemberAccess = false;
                    }

                    return(e);
                }

                throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString()));
            }
Beispiel #6
0
        /// <summary>
        /// Serializes an MemberExpression to a string
        /// </summary>
        /// <param name="m">Expression to serialize</param>
        /// <returns>MemberExpression</returns>
        internal override Expression VisitMemberAccess(MemberExpression m)
        {
            if (m.Member is FieldInfo)
            {
                throw new NotSupportedException(Strings.ALinq_CantReferToPublicField(m.Member.Name));
            }

            Expression e = this.Visit(m.Expression);

            // if this is a Nullable<T> instance, don't write out /Value since not supported by server
            if (m.Member.Name == "Value" && m.Member.DeclaringType.IsGenericType() &&
                m.Member.DeclaringType.GetGenericTypeDefinition() == typeof(Nullable <>))
            {
                return(m);
            }

            // if this is a GetValueAsync().Result call in async scenario, don't write out /Result
            MethodCallExpression methodCallExpression = m.Expression as MethodCallExpression;

            if (methodCallExpression != null && methodCallExpression.Method.Name == "GetValueAsync" && m.Member.Name == "Result")
            {
                return(m);
            }

            if (!this.IsImplicitInputReference(e) || writingFunctionsInQuery)
            {
                this.builder.Append(UriHelper.FORWARDSLASH);
            }

            // If the member is collection with count property, it will be parsed as $count segment
            var  parentType     = m.Member.DeclaringType;
            Type collectionType = ClientTypeUtil.GetImplementationType(parentType, typeof(ICollection <>));

            if (!PrimitiveType.IsKnownNullableType(parentType) && collectionType != null &&
                m.Member.Name.Equals(ReflectionUtil.COUNTPROPNAME, StringComparison.Ordinal))
            {
                this.builder.Append(UriHelper.DOLLARSIGN).Append(UriHelper.COUNT);
            }
            else
            {
                this.builder.Append(ClientTypeUtil.GetServerDefinedName(m.Member));
            }

            return(m);
        }
Beispiel #7
0
        /// <summary>
        /// Get or create a client EDM type instance.
        /// </summary>
        /// <param name="type">type to wrap</param>
        /// <returns>client type</returns>
        internal IEdmType GetOrCreateEdmType(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");
            this.ValidateComplexTypeHasProperties(type, cachedEdmType);
            return(cachedEdmType.EdmType);
        }
Beispiel #8
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))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Beispiel #9
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?");

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

            this.materializer = ODataMaterializer.CreateMaterializerForMessage(responseMessage, responseInfo, materializerType, queryComponents, plan, payloadKind);
        }
        /// <summary>
        /// CODE: x
        /// ORIGINAL: Convert(x, t) where t is assignable from typeof(x)
        /// ORIGINAL: x as t, where t is assignable from typeof(x)
        /// ORIGINAL: and typeof(x) or t are not known primitives unless typeof(x) == t
        /// ORIGINAL: and x is not a collection of entity types
        /// ORIGINAL: and x is not a enum type
        /// NORMALIZED: x
        /// </summary>
        internal override Expression VisitUnary(UnaryExpression u)
        {
            UnaryExpression visited = (UnaryExpression)base.VisitUnary(u);
            Expression      result  = visited;

            // Note that typically we would record a potential rewrite
            // after extracting the conversion, but we avoid doing this
            // because it breaks undoing the rewrite by making the non-local
            // change circular, ie:
            //   unary [operand = a]
            // becomes
            //   a <- unary [operand = a]
            // So the undoing visits a, then the original unary, then the
            // operand and again the unary, the operand, etc.
            this.RecordRewrite(u, result);

            // Convert(x, t) or x as t, where t is assignable from typeof(x)
            if ((visited.NodeType == ExpressionType.Convert || visited.NodeType == ExpressionType.TypeAs) && visited.Type.IsAssignableFrom(visited.Operand.Type))
            {
                // typeof(x) or t are not known primitives unless typeof(x) == t
                if (!PrimitiveType.IsKnownNullableType(visited.Operand.Type) && !PrimitiveType.IsKnownNullableType(visited.Type) || visited.Operand.Type == visited.Type)
                {
                    // x is not a collection of entity types
                    if (!(ClientTypeUtil.TypeOrElementTypeIsEntity(visited.Operand.Type) && ProjectionAnalyzer.IsCollectionProducingExpression(visited.Operand)))
                    {
                        // x is not an enum type
                        if (!visited.Operand.Type.IsEnum)
                        {
                            result = visited.Operand;
                        }
                    }
                }
            }

            return(result);
        }
Beispiel #11
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.GetOrCreateEdmType(elementType);

                    // Note that
                    // 1. throw here because collection of a collection is not allowed
                    // 2. will also throw during SaveChanges(), validated by unit test case 'SerializationOfCollection'in CollectionTests.cs.
                    if ((itemType.TypeKind == EdmTypeKind.Collection))
                    {
                        throw new ODataException(Strings.ClientType_CollectionOfCollectionNotSupported);
                    }

                    cachedEdmType = new EdmTypeCacheValue(new EdmCollectionType(itemType.ToEdmTypeReference(ClientTypeUtil.CanAssignNull(elementType))), hasProperties);
                }
                else
                {
                    Type enumTypeTmp = null;
                    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");
                        bool hasStream = GetHasStreamValue((IEdmEntityType)edmBaseType, type);
                        cachedEdmType = new EdmTypeCacheValue(
                            new EdmEntityTypeWithDelayLoadedProperties(CommonUtil.GetModelTypeNamespace(type), CommonUtil.GetModelTypeName(type), (IEdmEntityType)edmBaseType, c.PlatformHelper.IsAbstract(type), /*isOpen*/ false, hasStream, delayLoadEntityProperties),
                            hasProperties);
                    }
                    else if ((enumTypeTmp = Nullable.GetUnderlyingType(type) ?? type) != null &&
                             enumTypeTmp.IsEnum())
                    {
                        Action <EdmEnumTypeWithDelayLoadedMembers> delayLoadEnumMembers = (enumType) =>
                        {
#if WINRT
                            foreach (FieldInfo tmp in enumTypeTmp.GetFields().Where(fieldInfo => fieldInfo.IsStatic))
#else
                            foreach (FieldInfo tmp in enumTypeTmp.GetFields(BindingFlags.Static | BindingFlags.Public))
#endif
                            {
                                object memberValue = Enum.Parse(enumTypeTmp, tmp.Name, false);
                                enumType.AddMember(new EdmEnumMember(enumType, tmp.Name, new EdmIntegerConstant((long)Convert.ChangeType(memberValue, typeof(long), CultureInfo.InvariantCulture.NumberFormat))));
                            }
                        };

                        // underlying type may be Edm.Byte, Edm.SByte, Edm.Int16, Edm.Int32, or Edm.Int64.
                        Type underlyingType = Enum.GetUnderlyingType(enumTypeTmp);
                        IEdmPrimitiveType underlyingEdmType = (IEdmPrimitiveType)EdmCoreModel.Instance.FindDeclaredType("Edm." + underlyingType.Name);
                        Debug.Assert(underlyingEdmType != null, "underlyingEdmType != null");
                        bool isFlags = enumTypeTmp.GetCustomAttributes(false).Any(s => s is FlagsAttribute);
                        cachedEdmType = new EdmTypeCacheValue(
                            new EdmEnumTypeWithDelayLoadedMembers(CommonUtil.GetModelTypeNamespace(enumTypeTmp), CommonUtil.GetModelTypeName(enumTypeTmp), underlyingEdmType, isFlags, delayLoadEnumMembers),
                            null);
                    }
                    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), /*isOpen*/ false, 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);
        }