private void AddScalarMember(Type type, PropertyInfo clrProperty, StructuralType ospaceType, EdmProperty cspaceProperty, EdmType propertyType) { Debug.Assert(type != null, "type != null"); Debug.Assert(clrProperty != null, "clrProperty != null"); Debug.Assert(clrProperty.CanRead && clrProperty.CanWrite, "The clr property has to have a setter and a getter."); Debug.Assert(ospaceType != null, "ospaceType != null"); Debug.Assert(cspaceProperty != null, "cspaceProperty != null"); Debug.Assert(propertyType != null, "propertyType != null"); Debug.Assert(Helper.IsScalarType(propertyType), "Property has to be primitive or enum."); var cspaceType = cspaceProperty.DeclaringType; bool isKeyMember = Helper.IsEntityType(cspaceType) && ((EntityType)cspaceType).KeyMemberNames.Contains(clrProperty.Name); // the property is nullable only if it is not a key and can actually be set to null (i.e. is not a value type or is a nullable value type) bool nullableFacetValue = !isKeyMember && (!clrProperty.PropertyType.IsValueType || Nullable.GetUnderlyingType(clrProperty.PropertyType) != null); EdmProperty ospaceProperty = new EdmProperty( cspaceProperty.Name, TypeUsage.Create(propertyType, new FacetValues { Nullable = nullableFacetValue }), clrProperty, type.TypeHandle); if (isKeyMember) { ((EntityType)ospaceType).AddKeyMember(ospaceProperty); } else { ospaceType.AddMember(ospaceProperty); } }
private void CreateAndAddNavigationProperty(StructuralType cspaceType, StructuralType ospaceType, NavigationProperty cspaceProperty, PropertyInfo clrProperty) { EdmType ospaceRelationship; if (SessionData.CspaceToOspace.TryGetValue(cspaceProperty.RelationshipType, out ospaceRelationship)) { Debug.Assert(ospaceRelationship is StructuralType, "Structural type expected."); bool foundTarget = false; EdmType targetType = null; if (Helper.IsCollectionType(cspaceProperty.TypeUsage.EdmType)) { EdmType findType; foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)((CollectionType)cspaceProperty.TypeUsage.EdmType).TypeUsage.EdmType, out findType); if (foundTarget) { Debug.Assert(findType is StructuralType, "Structural type expected."); targetType = findType.GetCollectionType(); } } else { EdmType findType; foundTarget = SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out findType); if (foundTarget) { Debug.Assert(findType is StructuralType, "Structural type expected."); targetType = findType; } } Debug.Assert(foundTarget, "Since the relationship will only be created if it can find the types for both ends, we will never fail to find one of the ends"); NavigationProperty navigationProperty = new NavigationProperty(cspaceProperty.Name, TypeUsage.Create(targetType), clrProperty); navigationProperty.RelationshipType = (RelationshipType)ospaceRelationship; // we can use First because o-space relationships are created directly from // c-space relationship navigationProperty.ToEndMember = (RelationshipEndMember)((RelationshipType)ospaceRelationship).Members.First(e => e.Name == cspaceProperty.ToEndMember.Name); navigationProperty.FromEndMember = (RelationshipEndMember)((RelationshipType)ospaceRelationship).Members.First(e => e.Name == cspaceProperty.FromEndMember.Name); ospaceType.AddMember(navigationProperty); } else { EntityTypeBase missingType = cspaceProperty.RelationshipType.RelationshipEndMembers.Select(e => ((RefType)e.TypeUsage.EdmType).ElementType).First(e => e != cspaceType); string message = SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs( Strings.Validator_OSpace_Convention_RelationshipNotLoaded(cspaceProperty.RelationshipType.FullName, missingType.FullName), missingType); SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType)); } }
/// <summary> /// Resolves enum type property. /// </summary> /// <param name="declaringType">The type to add the declared property to.</param> /// <param name="clrProperty">Property to resolve.</param> private void ResolveEnumTypeProperty(StructuralType declaringType, PropertyInfo clrProperty) { Debug.Assert(declaringType != null, "type != null"); Debug.Assert(clrProperty != null, "clrProperty != null"); Debug.Assert( (Nullable.GetUnderlyingType(clrProperty.PropertyType) ?? clrProperty.PropertyType).IsEnum, "This method should be called for enums only"); EdmType propertyType; if (!TryGetLoadedType(clrProperty.PropertyType, out propertyType) || !Helper.IsEnumType(propertyType)) { SessionData.EdmItemErrors.Add( new EdmItemError( System.Data.Entity.Strings.Validator_OSpace_ScalarPropertyNotPrimitive( clrProperty.Name, clrProperty.DeclaringType.FullName, clrProperty.PropertyType.FullName), null)); } else { var edmScalarPropertyAttribute = (EdmScalarPropertyAttribute)clrProperty.GetCustomAttributes(typeof(EdmScalarPropertyAttribute), false).Single(); EdmProperty enumProperty = new EdmProperty( clrProperty.Name, TypeUsage.Create(propertyType, new FacetValues() { Nullable = edmScalarPropertyAttribute.IsNullable }), clrProperty, declaringType.ClrType.TypeHandle); declaringType.AddMember(enumProperty); if (declaringType.BuiltInTypeKind == BuiltInTypeKind.EntityType && edmScalarPropertyAttribute.EntityKeyProperty) { ((EntityType)declaringType).AddKeyMember(enumProperty); } } }
private void CreateAndAddComplexType(Type type, StructuralType ospaceType, EdmProperty cspaceProperty, PropertyInfo clrProperty) { EdmType propertyType; if (SessionData.CspaceToOspace.TryGetValue((StructuralType)cspaceProperty.TypeUsage.EdmType, out propertyType)) { Debug.Assert(propertyType is StructuralType, "Structural type expected."); EdmProperty property = new EdmProperty(cspaceProperty.Name, TypeUsage.Create(propertyType, new FacetValues { Nullable = false }), clrProperty, type.TypeHandle); ospaceType.AddMember(property); } else { string message = SessionData.LoadMessageLogger.CreateErrorMessageWithTypeSpecificLoadLogs( Strings.Validator_OSpace_Convention_MissingOSpaceType(cspaceProperty.TypeUsage.EdmType.FullName), cspaceProperty.TypeUsage.EdmType); SessionData.EdmItemErrors.Add(new EdmItemError(message, ospaceType)); } }
private void ResolveComplexTypeProperty(StructuralType type, PropertyInfo clrProperty) { // Load the property type and create a new property object EdmType propertyType; // If the type could not be loaded it's definitely not a complex type, so that's an error // If it could be loaded but is not a complex type that's an error as well if (!TryGetLoadedType(clrProperty.PropertyType, out propertyType) || propertyType.BuiltInTypeKind != BuiltInTypeKind.ComplexType) { // This property does not need to be validated further, just add to the errors collection and continue with the next property // This failure will cause an exception to be thrown later during validation of all of the types SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_ComplexPropertyNotComplex(clrProperty.Name, clrProperty.DeclaringType.FullName, clrProperty.PropertyType.FullName), null)); } else { EdmProperty newProperty = new EdmProperty(clrProperty.Name, TypeUsage.Create(propertyType, new FacetValues { Nullable = false }), clrProperty, type.ClrType.TypeHandle); type.AddMember(newProperty); } }
internal void ResolveNavigationProperty(StructuralType declaringType, PropertyInfo propertyInfo) { Debug.Assert(propertyInfo.IsDefined(typeof(EdmRelationshipNavigationPropertyAttribute), false), "The property must have navigation property defined"); // EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute // are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute // we will just ignore it and skip to the next property. object[] relationshipPropertyAttributes = propertyInfo.GetCustomAttributes(typeof(EdmRelationshipNavigationPropertyAttribute), false); Debug.Assert(relationshipPropertyAttributes.Length == 1, "There should be exactly one property for every navigation property"); // The only valid return types from navigation properties are: // (1) EntityType // (2) CollectionType containing valid EntityType // If TryGetLoadedType returned false, it could mean that we couldn't validate any part of the type, or it could mean that it's a generic // where the main generic type was validated, but the generic type parameter was not. We can't tell the difference, so just fail // with the same error message in both cases. The user will have to figure out which part of the type is wrong. // We can't just rely on checking for a generic because it can lead to a scenario where we report that the type parameter is invalid // when really it's the main generic type. That is more confusing than reporting the full name and letting the user determine the problem. EdmType propertyType; if (!TryGetLoadedType(propertyInfo.PropertyType, out propertyType) || !(propertyType.BuiltInTypeKind == BuiltInTypeKind.EntityType || propertyType.BuiltInTypeKind == BuiltInTypeKind.CollectionType)) { // Once an error is detected the property does not need to be validated further, just add to the errors // collection and continue with the next property. The failure will cause an exception to be thrown later during validation of all of the types. SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.Validator_OSpace_InvalidNavPropReturnType(propertyInfo.Name, propertyInfo.DeclaringType.FullName, propertyInfo.PropertyType.FullName), null)); return; } // else we have a valid EntityType or CollectionType that contains EntityType. ResolveNonSchemaType enforces that a collection type // must contain an EntityType, and if it doesn't, propertyType will be null here. If propertyType is EntityType or CollectionType we know it is valid // Expecting EdmRelationshipNavigationPropertyAttribute to have AllowMultiple=False, so only look at first element in the attribute array EdmRelationshipNavigationPropertyAttribute attribute = (EdmRelationshipNavigationPropertyAttribute)relationshipPropertyAttributes[0]; EdmMember member = null; EdmType type; if (SessionData.TypesInLoading.TryGetValue(attribute.RelationshipNamespaceName + "." + attribute.RelationshipName, out type) && Helper.IsAssociationType(type)) { AssociationType relationshipType = (AssociationType)type; if (relationshipType != null) { // The return value of this property has been verified, so create the property now NavigationProperty navigationProperty = new NavigationProperty(propertyInfo.Name, TypeUsage.Create(propertyType), propertyInfo); navigationProperty.RelationshipType = relationshipType; member = navigationProperty; if (relationshipType.Members[0].Name == attribute.TargetRoleName) { navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[0]; navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[1]; } else if (relationshipType.Members[1].Name == attribute.TargetRoleName) { navigationProperty.ToEndMember = (RelationshipEndMember)relationshipType.Members[1]; navigationProperty.FromEndMember = (RelationshipEndMember)relationshipType.Members[0]; } else { SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.TargetRoleNameInNavigationPropertyNotValid( propertyInfo.Name, propertyInfo.DeclaringType.FullName, attribute.TargetRoleName, attribute.RelationshipName), navigationProperty)); member = null; } if (member != null && ((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType != declaringType.ClrType) { SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.NavigationPropertyRelationshipEndTypeMismatch( declaringType.FullName, navigationProperty.Name, relationshipType.FullName, navigationProperty.FromEndMember.Name, ((RefType)navigationProperty.FromEndMember.TypeUsage.EdmType).ElementType.ClrType), navigationProperty)); member = null; } } } else { SessionData.EdmItemErrors.Add(new EdmItemError(System.Data.Entity.Strings.RelationshipNameInNavigationPropertyNotValid( propertyInfo.Name, propertyInfo.DeclaringType.FullName, attribute.RelationshipName), declaringType)); } if (member != null) { declaringType.AddMember(member); } }
/// <summary> /// Load all the property metadata of the given type /// </summary> /// <param name="type">The CLR entity type</param> /// <param name="structuralType">The type where properties are loaded</param> /// <param name="context"></param> private void LoadPropertiesFromType(StructuralType structuralType) { // Look at both public, internal, and private instanced properties declared at this type, inherited members // are not looked at. Internal and private properties are also looked at because they are also schematized fields PropertyInfo[] properties = structuralType.ClrType.GetProperties(PropertyReflectionBindingFlags); foreach (PropertyInfo property in properties) { EdmMember newMember = null; bool isEntityKeyProperty = false; //used for EdmScalarProperties only // EdmScalarPropertyAttribute, EdmComplexPropertyAttribute and EdmRelationshipNavigationPropertyAttribute // are all EdmPropertyAttributes that we need to process. If the current property is not an EdmPropertyAttribute // we will just ignore it and skip to the next property. if (property.IsDefined(typeof(EdmRelationshipNavigationPropertyAttribute), false)) { // keep the loop var from being lifted PropertyInfo pi = property; _unresolvedNavigationProperties.Add(() => ResolveNavigationProperty(structuralType, pi)); } else if (property.IsDefined(typeof(EdmScalarPropertyAttribute), false)) { if ((Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType).IsEnum) { TrackClosure(property.PropertyType); PropertyInfo local = property; AddTypeResolver(() => ResolveEnumTypeProperty(structuralType, local)); } else { newMember = LoadScalarProperty(structuralType.ClrType, property, out isEntityKeyProperty); } } else if (property.IsDefined(typeof(EdmComplexPropertyAttribute), false)) { TrackClosure(property.PropertyType); // keep loop var from being lifted PropertyInfo local = property; AddTypeResolver(() => ResolveComplexTypeProperty(structuralType, local)); } if (newMember == null) { // Property does not have one of the following attributes: // EdmScalarPropertyAttribute, EdmComplexPropertyAttribute, EdmRelationshipNavigationPropertyAttribute // This means its an unmapped property and can be ignored. // Or there were error encountered while loading the properties continue; } // Add the property object to the type structuralType.AddMember(newMember); // Add to the entity's collection of key members // Do this here instead of in the if condition above for scalar properties because // we want to make sure the AddMember call above did not fail before updating the key members if (Helper.IsEntityType(structuralType) && isEntityKeyProperty) { ((EntityType)structuralType).AddKeyMember(newMember); } } }