public void Find_RelatedAttributeAliasedEntity_MatchesExpected() { var entityReference = new EntityReference { EntityType = typeof(ChildRaisedRow) }; var definitionProvider = new DataAnnotationsDefinitionProvider(); var target = new EntityDefinition(definitionProvider, entityReference); var relationReference = new EntityReference { EntityType = typeof(SubRow), EntityAlias = "SubEntity" }; var attributeLocation = definitionProvider.GetEntityLocation(relationReference); var attributePath = new LinkedList <EntityLocation>(); attributePath.AddLast(attributeLocation); var expected = new EntityAttributeDefinition( attributePath, relationReference.EntityType.GetProperty(nameof(SubRow.FakeSubEntityId), BindingFlags.Public | BindingFlags.Instance), nameof(SubRow.FakeSubEntityId), EntityAttributeTypes.RelatedAutoNumberKey, 1, $"SubEntity.{nameof(SubRow.FakeSubEntityId)}"); var actual = target.Find(CreateExpression <ChildRaisedRow, int>(row => row.ComplexEntity.SubEntity.FakeSubEntityId)); Assert.AreEqual(expected, actual, string.Join(Environment.NewLine, expected.GetDifferences(actual))); }
/// <summary> /// Gets the base object for the attribute so that the correct POCO is targeted by the attribute delegate. /// </summary> /// <param name="item"> /// The root-level POCO item. /// </param> /// <param name="attribute"> /// The attribute to get the base object for. /// </param> /// <returns> /// The attribute's base <see cref="object"/>. /// </returns> private static object GetBaseObject(object item, EntityAttributeDefinition attribute) { var baseObject = item; var currentNode = attribute.EntityNode.List.First; var targetNode = attribute.EntityNode; while (currentNode != targetNode && currentNode?.Next != null && currentNode.Next.Value.IsVirtual == false) { var entityLocation = currentNode.Next.Value; // If the base object can't be found, the row has been flattened, so return the original item. baseObject = GetObjectProperty(baseObject, entityLocation) ?? item; currentNode = currentNode.Next; } return(baseObject); }
/// <summary> /// Gets the attributes for a relation. /// </summary> /// <param name="entityType"> /// The entity type. /// </param> /// <returns> /// An <see cref="IEnumerable{T}"/> of <see cref="EntityAttributeDefinition"/> items for the <paramref name="entityType"/>. /// </returns> private IEnumerable <EntityAttributeDefinition> GetAttributeDefinitions(Type entityType) { var entityReference = new EntityReference { EntityType = entityType }; var entityLocation = this.GetEntityLocation(entityReference); var entityPath = new LinkedList <EntityLocation>(); entityPath.AddLast(entityLocation); var attributeReferences = this.GetAttributeReferences(entityType); foreach (var attributeReference in attributeReferences) { var physicalName = this.GetPhysicalName(attributeReference.PropertyInfo); var ordinal = this.GetOrdinal(attributeReference.PropertyInfo); var attributeName = physicalName; var isPrimaryKey = this.IsKey(attributeReference.PropertyInfo); var isIdentity = this.IsIdentity(attributeReference.PropertyInfo); var isComputed = this.IsComputed(attributeReference.PropertyInfo); var attributeTypes = EntityAttributeTypes.None; if (isPrimaryKey) { attributeTypes |= EntityAttributeTypes.PrimaryKey; } if (isIdentity) { attributeTypes |= EntityAttributeTypes.IdentityColumn; } if (isComputed) { attributeTypes |= EntityAttributeTypes.Computed; } if (attributeReference.IsRelatedAttribute) { attributeTypes |= EntityAttributeTypes.ExplicitRelatedAttribute; var relatedEntityReference = new EntityReference { EntityType = attributeReference.EntityReference.EntityType, ContainerType = entityType, EntityAlias = attributeReference.EntityReference.EntityAlias }; var relatedLocation = this.GetEntityLocation(relatedEntityReference); // This is not a physical object on the POCO, so we indicate it as virtual. relatedLocation.IsVirtual = true; entityPath.AddLast(relatedLocation); // Use the physical name if overridden. physicalName = attributeReference.PhysicalName ?? physicalName; var entityAttributeDefinition = new EntityAttributeDefinition( entityPath, attributeReference.PropertyInfo, physicalName, attributeTypes, int.MaxValue, attributeReference.PropertyInfo.Name == physicalName ? null : attributeReference.PropertyInfo.Name); entityPath.RemoveLast(); yield return(entityAttributeDefinition); } else if (attributeReference.IsRelation) { attributeTypes |= EntityAttributeTypes.Relation; // Include the relation itself for quick access to getter/setter methods. var entityAttributeDefinition = new EntityAttributeDefinition( entityPath, attributeReference.PropertyInfo, attributeName, attributeTypes, ordinal, attributeReference.Name); yield return(entityAttributeDefinition); foreach (var definition in this.GetAttributeDefinitions(entityPath, attributeReference.PropertyInfo)) { yield return(definition); } } else { attributeTypes |= EntityAttributeTypes.DirectAttribute; var entityAttributeDefinition = new EntityAttributeDefinition( entityPath, attributeReference.PropertyInfo, attributeName, attributeTypes, ordinal); yield return(entityAttributeDefinition); } } }
/// <summary> /// Gets the entity definitions for the specified entity property. /// </summary> /// <param name="entityPath"> /// The entity path. /// </param> /// <param name="entityProperty"> /// The entity property to evaluate. /// </param> /// <returns> /// An <see cref="IEnumerable{T}"/> of <see cref="EntityAttributeDefinition"/> items. /// </returns> private IEnumerable <EntityAttributeDefinition> GetAttributeDefinitions(LinkedList <EntityLocation> entityPath, PropertyInfo entityProperty) { var entityType = entityProperty.PropertyType; var relationReference = new EntityReference { EntityType = entityType, ContainerType = entityPath.First?.Value.EntityType, EntityAlias = entityProperty.Name }; var relationLocation = this.GetEntityLocation(relationReference); entityPath.AddLast(relationLocation); var entityProperties = this.GetFilteredEntityProperties(entityType).ToList(); // Do columns first. foreach (var propertyInfo in this.GetDirectPropertyInfos(entityProperties)) { var propertyName = propertyInfo.Name; var physicalName = this.GetPhysicalName(propertyInfo); var propertyAlias = string.Concat(relationLocation.Alias ?? relationLocation.Name, '.', propertyName); var ordinal = this.GetOrdinal(propertyInfo); var isPrimaryKey = this.IsKey(propertyInfo); var isIdentity = this.IsIdentity(propertyInfo); var isComputed = this.IsComputed(propertyInfo); var attributeTypes = EntityAttributeTypes.RelatedAttribute; if (isPrimaryKey) { attributeTypes |= EntityAttributeTypes.PrimaryKey; } if (isIdentity) { attributeTypes |= EntityAttributeTypes.IdentityColumn; } if (isComputed) { attributeTypes |= EntityAttributeTypes.Computed; } var entityAttributeDefinition = new EntityAttributeDefinition( entityPath, propertyInfo, physicalName, attributeTypes, ordinal, propertyName == propertyAlias ? null : propertyAlias); yield return(entityAttributeDefinition); } // Next, handle direct related entity attributes. foreach (var propertyInfo in this.GetRelatedColumnPropertyInfos(entityProperties)) { var attributeReference = GetRelatedEntityAttributeReference(propertyInfo); // This is not a physical object on the POCO, so we indicate it as virtual. var relatedLocation = this.GetEntityLocation(attributeReference.EntityReference); relatedLocation.IsVirtual = true; entityPath.AddLast(relatedLocation); // Adding the dot avoids collisions with FKs. var propertyAlias = string.Concat(relationLocation.Alias ?? relationLocation.Name, '.', attributeReference.Name); var entityAttributeDefinition = new EntityAttributeDefinition( entityPath, propertyInfo, attributeReference.PhysicalName, EntityAttributeTypes.RelatedAttribute, int.MaxValue, attributeReference.Name == propertyAlias ? null : propertyAlias); entityPath.RemoveLast(); yield return(entityAttributeDefinition); } foreach (var propertyInfo in this.GetRelationPropertyInfos(entityProperties)) { var attributeName = this.GetPhysicalName(propertyInfo); // Include the relation itself for quick access to getter/setter methods. var relationAttribute = new EntityAttributeDefinition( entityPath, propertyInfo, attributeName, EntityAttributeTypes.Relation, int.MaxValue, propertyInfo.Name); yield return(relationAttribute); foreach (var entityAttributeDefinition in this.GetAttributeDefinitions(entityPath, propertyInfo)) { yield return(entityAttributeDefinition); } } entityPath.RemoveLast(); }
/// <summary> /// Gets the canonical attribute name. /// </summary> /// <param name="attribute"> /// The attribute to get the name for. /// </param> /// <returns> /// The physical name of the attribute, or the alias, if the attribute is aliased. <see cref="string"/>. /// </returns> private string GetAttributeCanonicalName(EntityAttributeDefinition attribute) { return(this.Escape(attribute.Alias ?? attribute.PhysicalName)); }
/// <inheritdoc /> public string GetCanonicalName(EntityAttributeDefinition attribute) { return($"{this.GetPhysicalName(attribute.Entity)}.{this.GetAttributeCanonicalName(attribute)}"); }
/// <inheritdoc /> public string GetReferenceName(EntityAttributeDefinition attribute) { return($"{this.GetEntityCanonicalName(attribute.Entity)}.{this.Escape(attribute.PhysicalName)}"); }
/// <inheritdoc /> public string Qualify(EntityAttributeDefinition attribute, EntityLocation entityLocation) { return($"{this.GetEntityCanonicalName(entityLocation)}.{this.GetAttributeCanonicalName(attribute)}"); }
/// <inheritdoc /> public string Qualify(EntityAttributeDefinition attribute) { return(this.Qualify(attribute, attribute.Entity)); }