/// <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; }
/// <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); } }
/// <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); }
/// <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); }
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); }