Example #1
0
        /// <summary>
        /// Tries to get a well known PrimitiveType for a clr type. Contains logic to handle Binary type.
        /// </summary>
        /// <param name="clrType">The clr type to get well known PrimitiveType for.</param>
        /// <param name="ptype">PrimitiveType for the <paramref name="clrType"/> if exists. Otherwise null.</param>
        /// <returns><c>true</c> if a PrimitiveType for the <paramref name="clrType"/> was found. Otherwise <c>false</c>.</returns>
        private static bool TryGetWellKnownPrimitiveType(Type clrType, out PrimitiveType ptype)
        {
            Debug.Assert(clrType != null, "clrType != null");

            ptype = null;
            if (!clrMapping.TryGetValue(clrType, out ptype))
            {
#if !PORTABLELIB
                if (IsBinaryType(clrType))
                {
                    Debug.Assert(clrMapping.ContainsKey(typeof(BinaryTypeSub)), "BinaryTypeSub missing from the dictionary");
                    ptype = clrMapping[typeof(BinaryTypeSub)];
                }
#endif
            }

            return ptype != null;
        }
Example #2
0
        /// <summary>
        /// Register a known type as primitive type
        /// </summary>
        /// <param name="clrType">The Clr Type</param>
        /// <param name="edmTypeName">The Edm Type Name</param>
        /// <param name="primitiveKind">The Edm Primitive Type Kind</param>
        /// <param name="converter">The Type Converter</param>
        /// <param name="twoWay">Whether this mapping should have a reverse mapping from Edm</param>
        /// <remarks>
        /// This method is internal only for testing purposes. 
        /// IN PRODUCT MUST BE CALLED ONLY FROM THE STATIC CTOR OF THE PrimitiveType CLASS.
        /// </remarks>
        internal static void RegisterKnownType(Type clrType, string edmTypeName, EdmPrimitiveTypeKind primitiveKind, PrimitiveTypeConverter converter, bool twoWay)
        {
            Debug.Assert(!clrMapping.ContainsKey(clrType), "Clr type already registered");
            Debug.Assert(clrType != null, "clrType != null");
            Debug.Assert(primitiveKind != EdmPrimitiveTypeKind.None, "primitiveKind != EdmPrimitiveTypeKind.None");
            Debug.Assert(converter != null, "converter != null");

            PrimitiveType pt = new PrimitiveType(clrType, edmTypeName, primitiveKind, converter, twoWay);
            clrMapping.Add(clrType, pt);

            if (twoWay)
            {
                Debug.Assert(!edmMapping.ContainsKey(edmTypeName), "Edm type name already registered");
                edmMapping.Add(edmTypeName, pt);
            }
        }
Example #3
0
 /// <summary>
 /// Try retrieve a primitive type metadata from a Edm Type Name
 /// </summary>
 /// <param name="edmTypeName">Edm Type Name</param>
 /// <param name="ptype">The returning primitive type</param>
 /// <returns>True if the type is found</returns>
 internal static bool TryGetPrimitiveType(String edmTypeName, out PrimitiveType ptype)
 {
     return edmMapping.TryGetValue(edmTypeName, out ptype);
 }
Example #4
0
        /// <summary>
        /// Try retrieve a primitive type metadata from a clr type
        /// </summary>
        /// <param name="clrType">The Clr Type</param>
        /// <param name="ptype">The returning primitive type</param>
        /// <returns>True if the type is found</returns>
        /// <remarks>
        /// See remarks for the class. 
        /// </remarks>
        internal static bool TryGetPrimitiveType(Type clrType, out PrimitiveType ptype)
        {
            Type primitiveClrType = Nullable.GetUnderlyingType(clrType) ?? clrType;

            // Is it a well known primitive type?
            if (!TryGetWellKnownPrimitiveType(primitiveClrType, out ptype))
            {
                lock (knownNonPrimitiveTypes)
                {
                    // Is it a type that we know that is not primitive?
                    if (knownNonPrimitiveTypes.Contains(clrType))
                    {
                        ptype = null;
                        return false;
                    }
                }

                KeyValuePair<Type, PrimitiveType>[] possibleMatches;
                lock (derivedPrimitiveTypeMapping)
                {
                    // is it a derived primitive type?
                    if (derivedPrimitiveTypeMapping.TryGetValue(clrType, out ptype))
                    {
                        return true;
                    }

                    // note that clrMapping contains a substitute type for Binary. We don't really care about this type since you cannot
                    // derive from System.Data.Linq.Binary type as it is sealed. Also to get all other applicable types we need to concatenate
                    // well known primitive types and custom primitive types. We can exclude primitive and sealed types as they cannot be
                    // derived from.
                    possibleMatches = clrMapping.Where(m => !m.Key.IsPrimitive() && !m.Key.IsSealed()).Concat(derivedPrimitiveTypeMapping).ToArray();
                }

                var bestMatch = new KeyValuePair<Type, PrimitiveType>(typeof(object), null);
                foreach (var possibleMatch in possibleMatches)
                {
                    // If the primitive type is a sub class of the one of the known primitive types
                    // then its a possible match
                    if (primitiveClrType.IsSubclassOf(possibleMatch.Key))
                    {
                        // In order to find the most derived type for the best match, we need to check
                        // if the current match is a more derived type than the previous match
                        if (possibleMatch.Key.IsSubclassOf(bestMatch.Key))
                        {
                            bestMatch = possibleMatch;
                        }
                    }
                }

                if (bestMatch.Value == null)
                {
                    // this is not a primitive type - update the hashset accordingly. 
                    lock (knownNonPrimitiveTypes)
                    {
                        // Note that this is hashset so it is OK if we try adding the same type more than once. 
                        knownNonPrimitiveTypes.Add(clrType);
                    }

                    return false;
                }

                ptype = bestMatch.Value;
                lock (derivedPrimitiveTypeMapping)
                {
                    // this is a derived primitive type - update the dictionary accordingly
                    derivedPrimitiveTypeMapping[primitiveClrType] = ptype;
                }
            }

            return true;
        }
        /// <summary>
        /// Creates and returns an ODataCollectionValue from the given value.
        /// </summary>
        /// <param name="collectionItemType">The type of the value.</param>
        /// <param name="propertyName">If the value is a property, then it represents the name of the property. Can be null, for non-property.</param>
        /// <param name="value">The value.</param>
        /// <param name="visitedComplexTypeObjects">Set of instances of complex types encountered in the hierarchy. Used to detect cycles.</param>
        /// <param name="isDynamicProperty">Whether this collection property is a dynamic property</param>
        /// <param name="setTypeAnnotation">If true, set the type annotation on ODataValue.</param>
        /// <returns>An ODataCollectionValue representing the given value.</returns>
        internal ODataCollectionValue CreateODataCollection(Type collectionItemType, string propertyName, object value, HashSet <object> visitedComplexTypeObjects, bool isDynamicProperty, bool setTypeAnnotation = true)
        {
            Debug.Assert(collectionItemType != null, "collectionItemType != null");

            WebUtil.ValidateCollection(collectionItemType, value, propertyName, isDynamicProperty);

            PrimitiveType ptype;
            bool          isCollectionOfPrimitiveTypes = PrimitiveType.TryGetPrimitiveType(collectionItemType, out ptype);

            ODataCollectionValue collection = new ODataCollectionValue();
            IEnumerable          enumerablePropertyValue = (IEnumerable)value;
            string collectionItemTypeName;
            string collectionTypeName;

            if (isCollectionOfPrimitiveTypes)
            {
                collectionItemTypeName = ClientConvert.GetEdmType(Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType);

                if (enumerablePropertyValue != null)
                {
                    collection.Items = Util.GetEnumerable(
                        enumerablePropertyValue,
                        (val) =>
                    {
                        if (val == null)
                        {
                            return(null);
                        }

                        WebUtil.ValidatePrimitiveCollectionItem(val, propertyName, collectionItemType);
                        return(ConvertPrimitiveValueToRecognizedODataType(val, collectionItemType));
                    });
                }

                // TypeName for primitives should be the EDM name since that's what we will be able to look up in the model
                collectionTypeName = collectionItemTypeName;
            }
            else
            {
                Type collectionItemTypeTmp = Nullable.GetUnderlyingType(collectionItemType) ?? collectionItemType;
                bool areEnumItems          = collectionItemTypeTmp.IsEnum();

                // Note that the collectionItemTypeName will be null if the context does not have the ResolveName func.
                collectionItemTypeName = this.requestInfo.ResolveNameFromType(collectionItemType);

                if (enumerablePropertyValue != null)
                {
                    collection.Items = Util.GetEnumerable(
                        enumerablePropertyValue,
                        (val) =>
                    {
                        if (areEnumItems)
                        {
                            if (val == null)
                            {
                                return(new ODataEnumValue(null, collectionItemType.FullName) as ODataValue);
                            }

                            return(new ODataEnumValue(ClientTypeUtil.GetEnumValuesString(val.ToString(), collectionItemTypeTmp), collectionItemType.FullName) as ODataValue);
                        }
                        else
                        {
                            if (val == null)
                            {
                                return(null);
                            }

                            WebUtil.ValidateComplexCollectionItem(val, propertyName, collectionItemType);
                            return(this.CreateODataComplexValue(val.GetType(), val, propertyName, true /*isCollectionItem*/, visitedComplexTypeObjects)
                                   as ODataValue);
                        }
                    });
                }

                // TypeName for complex types needs to be the client type name (not the one we resolved above) since it will be looked up in the client model
                collectionTypeName = collectionItemType.FullName;
            }

            // Set the type name to use for client type lookups and validation. Because setting this value can cause validation to occur, we will
            // only do it for JSON Light, in order to avoid breaking changes with the WCF Data Services 5.0 release, since it was already shipped without this.
            if (!this.requestInfo.Format.UsingAtom)
            {
                collection.TypeName = GetCollectionName(collectionTypeName);
            }

            // Ideally, we should not set type annotation on collection value.
            // To keep backward compatibility, we'll keep it in request body, but do not include it in url.
            if (setTypeAnnotation)
            {
                string wireTypeName = GetCollectionName(collectionItemTypeName);
                collection.SetAnnotation(new SerializationTypeNameAnnotation {
                    TypeName = wireTypeName
                });
            }

            return(collection);
        }
Example #6
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;
                bool isOpen = false;
                if (EdmStructuredSchemaElements != null && EdmStructuredSchemaElements.Any())
                {
                    IEdmStructuredType edmStructuredType =
                        EdmStructuredSchemaElements.FirstOrDefault(
                            et => (et != null && et.Name == ClientTypeUtil.GetServerDefinedTypeName(type))) as IEdmStructuredType;
                    if (edmStructuredType != null)
                    {
                        isOpen = edmStructuredType.IsOpen;
                    }
                }

                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, hasStream, delayLoadEntityProperties),
                            hasProperties);
                    }
                    else if ((enumTypeTmp = Nullable.GetUnderlyingType(type) ?? type) != null &&
                             enumTypeTmp.IsEnum())
                    {
                        Action <EdmEnumTypeWithDelayLoadedMembers> delayLoadEnumMembers = (enumType) =>
                        {
#if DNXCORE50
                            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, 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);
        }