private static IEdmType GetEdmType(IEdmModel edmModel, Type clrType, bool testCollections) { Contract.Assert(edmModel != null); Contract.Assert(clrType != null); IEdmPrimitiveType primitiveType = GetEdmPrimitiveTypeOrNull(clrType); if (primitiveType != null) { return(primitiveType); } else { if (testCollections) { Type enumerableOfT = ExtractGenericInterface(clrType, typeof(IEnumerable <>)); if (enumerableOfT != null) { Type elementClrType = enumerableOfT.GetGenericArguments()[0]; // IEnumerable<SelectExpandWrapper<T>> is a collection of T. Type entityType; if (IsSelectExpandWrapper(elementClrType, out entityType)) { elementClrType = entityType; } IEdmType elementType = GetEdmType(edmModel, elementClrType, testCollections: false); if (elementType != null) { return(new EdmCollectionType(elementType.ToEdmTypeReference(IsNullable(elementClrType)))); } } } Type underlyingType = TypeHelper.GetUnderlyingTypeOrSelf(clrType); if (underlyingType.IsEnum) { clrType = underlyingType; } // search for the ClrTypeAnnotation and return it if present IEdmType returnType = edmModel .SchemaElements .OfType <IEdmType>() .Select(edmType => new { EdmType = edmType, Annotation = edmModel.GetAnnotationValue <ClrTypeAnnotation>(edmType) }) .Where(tuple => tuple.Annotation != null && tuple.Annotation.ClrType == clrType) .Select(tuple => tuple.EdmType) .SingleOrDefault(); // default to the EdmType with the same name as the ClrType name returnType = returnType ?? edmModel.FindType(clrType.EdmFullName()); if (clrType.BaseType != null) { // go up the inheritance tree to see if we have a mapping defined for the base type. returnType = returnType ?? GetEdmType(edmModel, clrType.BaseType, testCollections); } return(returnType); } }
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); }
/// <summary> /// Binds 'cast' function to create a LINQ <see cref="Expression"/>. /// </summary> /// <param name="node">The query node to bind.</param> /// <param name="context">The query binder context.</param> /// <returns>The LINQ <see cref="Expression"/> created.</returns> protected virtual Expression BindCastSingleValue(SingleValueFunctionCallNode node, QueryBinderContext context) { CheckArgumentNull(node, context, "cast"); Expression[] arguments = BindArguments(node.Parameters, context); Contract.Assert(arguments.Length == 1 || arguments.Length == 2); Expression source = arguments.Length == 1 ? context.CurrentParameter : arguments[0]; string targetTypeName = (string)((ConstantNode)node.Parameters.Last()).Value; IEdmType targetEdmType = context.Model.FindType(targetTypeName); Type targetClrType = null; if (targetEdmType != null) { IEdmTypeReference targetEdmTypeReference = targetEdmType.ToEdmTypeReference(false); targetClrType = context.Model.GetClrType(targetEdmTypeReference); if (source != NullConstant) { if (source.Type == targetClrType) { return(source); } if ((!targetEdmTypeReference.IsPrimitive() && !targetEdmTypeReference.IsEnum()) || (context.Model.GetEdmPrimitiveTypeReference(source.Type) == null && !TypeHelper.IsEnum(source.Type))) { // Cast fails and return null. return(NullConstant); } } } if (targetClrType == null || source == NullConstant) { return(NullConstant); } if (targetClrType == typeof(string)) { return(ExpressionBinderHelper.BindCastToStringType(source)); } else if (TypeHelper.IsEnum(targetClrType)) { return(BindCastToEnumType(source.Type, targetClrType, node.Parameters.First(), arguments.Length, context)); } else { if (TypeHelper.IsNullable(source.Type) && !TypeHelper.IsNullable(targetClrType)) { // Make the target Clr type nullable to avoid failure while casting // nullable source, whose value may be null, to a non-nullable type. // For example: cast(NullableInt32Property,Edm.Int64) // The target Clr type should be Nullable<Int64> rather than Int64. targetClrType = typeof(Nullable <>).MakeGenericType(targetClrType); } try { return(Expression.Convert(source, targetClrType)); } catch (InvalidOperationException) { // Cast fails and return null. return(NullConstant); } } }