public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityTypeBuilder) { Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); var entityType = entityTypeBuilder.Metadata; var navigationPairCandidates = new Dictionary<Type, Tuple<List<PropertyInfo>, List<PropertyInfo>>>(); if (entityType.HasClrType) { foreach (var navigationPropertyInfo in entityType.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) { var entityClrType = navigationPropertyInfo.FindCandidateNavigationPropertyType(); if (entityClrType == null || !entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.Convention)) { continue; } var targetEntityTypeBuilder = entityTypeBuilder.ModelBuilder.Entity(entityClrType, ConfigurationSource.Convention); if (targetEntityTypeBuilder == null) { continue; } // The navigation could have been added when the target entity type was added if (!entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.Convention)) { continue; } if (navigationPairCandidates.ContainsKey(targetEntityTypeBuilder.Metadata.ClrType)) { if (entityType != targetEntityTypeBuilder.Metadata || !navigationPairCandidates[targetEntityTypeBuilder.Metadata.ClrType].Item2.Contains(navigationPropertyInfo)) { navigationPairCandidates[targetEntityTypeBuilder.Metadata.ClrType].Item1.Add(navigationPropertyInfo); } continue; } var navigations = new List<PropertyInfo> { navigationPropertyInfo }; var reverseNavigations = new List<PropertyInfo>(); navigationPairCandidates[targetEntityTypeBuilder.Metadata.ClrType] = new Tuple<List<PropertyInfo>, List<PropertyInfo>>(navigations, reverseNavigations); foreach (var reversePropertyInfo in targetEntityTypeBuilder.Metadata.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) { var reverseEntityClrType = reversePropertyInfo.FindCandidateNavigationPropertyType(); if (reverseEntityClrType == null || !targetEntityTypeBuilder.CanAddNavigation(reversePropertyInfo.Name, ConfigurationSource.Convention) || entityType.ClrType != reverseEntityClrType || navigationPropertyInfo == reversePropertyInfo) { continue; } reverseNavigations.Add(reversePropertyInfo); } } foreach (var navigationPairCandidate in navigationPairCandidates) { var navigationCandidates = navigationPairCandidate.Value.Item1; var reverseNavigationCandidates = navigationPairCandidate.Value.Item2; if (navigationCandidates.Count > 1 && reverseNavigationCandidates.Count > 0) { // Ambiguous navigations return entityTypeBuilder; } if (reverseNavigationCandidates.Count > 1) { // Ambiguous navigations return entityTypeBuilder; } foreach (var navigationCandidate in navigationCandidates) { var targetEntityTypeBuilder = entityTypeBuilder.ModelBuilder.Entity(navigationCandidate.FindCandidateNavigationPropertyType(), ConfigurationSource.Convention); targetEntityTypeBuilder.Relationship(entityTypeBuilder, navigationCandidate, reverseNavigationCandidates.SingleOrDefault(), ConfigurationSource.Convention); } } } // While running conventions on entityType, its source will be DataAnnotation or higher // Which means that entity won't be removed while being configured even if it is unreachable // This takes care of removing such unreachable entities (being run after we are done building relationships using this entity) entityTypeBuilder.ModelBuilder.RemoveEntityTypesUnreachableByNavigations(ConfigurationSource.DataAnnotation); return entityTypeBuilder; }
public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityTypeBuilder) { Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); var entityType = entityTypeBuilder.Metadata; var navigationPairCandidates = new Dictionary<InternalEntityTypeBuilder, Tuple<List<PropertyInfo>, List<PropertyInfo>>>(); if (entityType.HasClrType) { foreach (var navigationPropertyInfo in entityType.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) { Type entityClrType; if (!navigationPropertyInfo.IsCandidateNavigationProperty(out entityClrType) || !entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.Convention)) { continue; } var targetEntityTypeBuilder = entityTypeBuilder.ModelBuilder.Entity(entityClrType, ConfigurationSource.Convention); if (targetEntityTypeBuilder == null) { continue; } // The navigation could have been added when the target entity type was added if (!entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.Convention)) { continue; } if (navigationPairCandidates.ContainsKey(targetEntityTypeBuilder)) { if (entityType != targetEntityTypeBuilder.Metadata || !navigationPairCandidates[targetEntityTypeBuilder].Item2.Contains(navigationPropertyInfo)) { navigationPairCandidates[targetEntityTypeBuilder].Item1.Add(navigationPropertyInfo); } continue; } var navigations = new List<PropertyInfo> { navigationPropertyInfo }; var reverseNavigations = new List<PropertyInfo>(); navigationPairCandidates[targetEntityTypeBuilder] = new Tuple<List<PropertyInfo>, List<PropertyInfo>>(navigations, reverseNavigations); foreach (var reversePropertyInfo in targetEntityTypeBuilder.Metadata.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) { Type reverseEntityClrType; if (!reversePropertyInfo.IsCandidateNavigationProperty(out reverseEntityClrType) || !targetEntityTypeBuilder.CanAddNavigation(reversePropertyInfo.Name, ConfigurationSource.Convention) || entityType.ClrType != reverseEntityClrType || navigationPropertyInfo == reversePropertyInfo) { continue; } reverseNavigations.Add(reversePropertyInfo); } } foreach (var navigationPairCandidate in navigationPairCandidates) { var targetEntityTypeBuilder = navigationPairCandidate.Key; var navigationCandidates = navigationPairCandidate.Value.Item1; var reverseNavigationCandidates = navigationPairCandidate.Value.Item2; if (navigationCandidates.Count > 1 && reverseNavigationCandidates.Count > 0) { // Ambiguous navigations return entityTypeBuilder; } if (reverseNavigationCandidates.Count > 1) { // Ambiguous navigations return entityTypeBuilder; } foreach (var navigationCandidate in navigationCandidates) { TryBuildRelationship(entityTypeBuilder, targetEntityTypeBuilder, navigationCandidate, reverseNavigationCandidates.SingleOrDefault()); } } } return entityTypeBuilder; }
public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityTypeBuilder) { Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); var entityType = entityTypeBuilder.Metadata; var navigationPairCandidates = new Dictionary <InternalEntityTypeBuilder, Tuple <List <PropertyInfo>, List <PropertyInfo> > >(); if (entityType.HasClrType) { foreach (var navigationPropertyInfo in entityType.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) { Type entityClrType; if (!navigationPropertyInfo.IsCandidateNavigationProperty(out entityClrType) || !entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.Convention)) { continue; } var targetEntityTypeBuilder = entityTypeBuilder.ModelBuilder.Entity(entityClrType, ConfigurationSource.Convention); if (targetEntityTypeBuilder == null) { continue; } // The navigation could have been added when the target entity type was added if (!entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.Convention)) { continue; } if (navigationPairCandidates.ContainsKey(targetEntityTypeBuilder)) { if (entityType != targetEntityTypeBuilder.Metadata || !navigationPairCandidates[targetEntityTypeBuilder].Item2.Contains(navigationPropertyInfo)) { navigationPairCandidates[targetEntityTypeBuilder].Item1.Add(navigationPropertyInfo); } continue; } var navigations = new List <PropertyInfo> { navigationPropertyInfo }; var reverseNavigations = new List <PropertyInfo>(); navigationPairCandidates[targetEntityTypeBuilder] = new Tuple <List <PropertyInfo>, List <PropertyInfo> >(navigations, reverseNavigations); foreach (var reversePropertyInfo in targetEntityTypeBuilder.Metadata.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) { Type reverseEntityClrType; if (!reversePropertyInfo.IsCandidateNavigationProperty(out reverseEntityClrType) || !targetEntityTypeBuilder.CanAddNavigation(reversePropertyInfo.Name, ConfigurationSource.Convention) || entityType.ClrType != reverseEntityClrType || navigationPropertyInfo == reversePropertyInfo) { continue; } reverseNavigations.Add(reversePropertyInfo); } } foreach (var navigationPairCandidate in navigationPairCandidates) { var targetEntityTypeBuilder = navigationPairCandidate.Key; var navigationCandidates = navigationPairCandidate.Value.Item1; var reverseNavigationCandidates = navigationPairCandidate.Value.Item2; if (navigationCandidates.Count > 1 && reverseNavigationCandidates.Count > 0) { // Ambiguous navigations return(entityTypeBuilder); } if (reverseNavigationCandidates.Count > 1) { // Ambiguous navigations return(entityTypeBuilder); } foreach (var navigationCandidate in navigationCandidates) { TryBuildRelationship(entityTypeBuilder, targetEntityTypeBuilder, navigationCandidate, reverseNavigationCandidates.SingleOrDefault()); } } } return(entityTypeBuilder); }
public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityTypeBuilder) { Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); var entityType = entityTypeBuilder.Metadata; var navigationPairCandidates = new Dictionary <Type, Tuple <List <PropertyInfo>, List <PropertyInfo> > >(); if (entityType.HasClrType) { foreach (var navigationPropertyInfo in entityType.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) { var entityClrType = navigationPropertyInfo.FindCandidateNavigationPropertyType(); if (entityClrType == null || !entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.Convention)) { continue; } var targetEntityTypeBuilder = entityTypeBuilder.ModelBuilder.Entity(entityClrType, ConfigurationSource.Convention); if (targetEntityTypeBuilder == null) { continue; } // The navigation could have been added when the target entity type was added if (!entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.Convention)) { continue; } if (navigationPairCandidates.ContainsKey(targetEntityTypeBuilder.Metadata.ClrType)) { if (entityType != targetEntityTypeBuilder.Metadata || !navigationPairCandidates[targetEntityTypeBuilder.Metadata.ClrType].Item2.Contains(navigationPropertyInfo)) { navigationPairCandidates[targetEntityTypeBuilder.Metadata.ClrType].Item1.Add(navigationPropertyInfo); } continue; } var navigations = new List <PropertyInfo> { navigationPropertyInfo }; var reverseNavigations = new List <PropertyInfo>(); navigationPairCandidates[targetEntityTypeBuilder.Metadata.ClrType] = new Tuple <List <PropertyInfo>, List <PropertyInfo> >(navigations, reverseNavigations); foreach (var reversePropertyInfo in targetEntityTypeBuilder.Metadata.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) { var reverseEntityClrType = reversePropertyInfo.FindCandidateNavigationPropertyType(); if (reverseEntityClrType == null || !targetEntityTypeBuilder.CanAddNavigation(reversePropertyInfo.Name, ConfigurationSource.Convention) || entityType.ClrType != reverseEntityClrType || navigationPropertyInfo == reversePropertyInfo) { continue; } reverseNavigations.Add(reversePropertyInfo); } } foreach (var navigationPairCandidate in navigationPairCandidates) { var navigationCandidates = navigationPairCandidate.Value.Item1; var reverseNavigationCandidates = navigationPairCandidate.Value.Item2; if (navigationCandidates.Count > 1 && reverseNavigationCandidates.Count > 0) { // Ambiguous navigations return(entityTypeBuilder); } if (reverseNavigationCandidates.Count > 1) { // Ambiguous navigations return(entityTypeBuilder); } foreach (var navigationCandidate in navigationCandidates) { var targetEntityTypeBuilder = entityTypeBuilder.ModelBuilder.Entity(navigationCandidate.FindCandidateNavigationPropertyType(), ConfigurationSource.Convention); targetEntityTypeBuilder.Relationship(entityTypeBuilder, navigationCandidate, reverseNavigationCandidates.SingleOrDefault(), ConfigurationSource.Convention); } } } // While running conventions on entityType, its source will be DataAnnotation or higher // Which means that entity won't be removed while being configured even if it is unreachable // This takes care of removing such unreachable entities (being run after we are done building relationships using this entity) entityTypeBuilder.ModelBuilder.RemoveEntityTypesUnreachableByNavigations(ConfigurationSource.DataAnnotation); return(entityTypeBuilder); }
public override InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityTypeBuilder, PropertyInfo navigationPropertyInfo, InversePropertyAttribute attribute) { Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); Check.NotNull(navigationPropertyInfo, nameof(navigationPropertyInfo)); Check.NotNull(attribute, nameof(attribute)); if (!entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.DataAnnotation)) { return(entityTypeBuilder); } var targetType = navigationPropertyInfo.FindCandidateNavigationPropertyType(); var targetEntityTypeBuilder = entityTypeBuilder.ModelBuilder.Entity(targetType, ConfigurationSource.DataAnnotation); if (targetEntityTypeBuilder == null) { return(entityTypeBuilder); } // The navigation could have been added when the target entity type was added if (!entityTypeBuilder.CanAddNavigation(navigationPropertyInfo.Name, ConfigurationSource.DataAnnotation)) { return(entityTypeBuilder); } var inverseNavigationPropertyInfo = targetType.GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, attribute.Property, StringComparison.OrdinalIgnoreCase)); if (inverseNavigationPropertyInfo == null || inverseNavigationPropertyInfo.FindCandidateNavigationPropertyType() != entityTypeBuilder.Metadata.ClrType) { throw new InvalidOperationException( Strings.InvalidNavigationWithInverseProperty(navigationPropertyInfo.Name, entityTypeBuilder.Metadata.ClrType, attribute.Property, targetType)); } if (inverseNavigationPropertyInfo == navigationPropertyInfo) { throw new InvalidOperationException( Strings.SelfReferencingNavigationWithInverseProperty( navigationPropertyInfo.Name, entityTypeBuilder.Metadata.ClrType, navigationPropertyInfo.Name, entityTypeBuilder.Metadata.ClrType)); } // Check for InversePropertyAttribute on the inverseNavigation to verify that it matches. var inverseAttribute = inverseNavigationPropertyInfo.GetCustomAttribute <InversePropertyAttribute>(true); if (inverseAttribute != null && inverseAttribute.Property != navigationPropertyInfo.Name) { // TODO: Log error that InversePropertyAttributes are not pointing at each other var inverseNavigation = targetEntityTypeBuilder.Metadata.FindNavigation(inverseNavigationPropertyInfo.Name); if (inverseNavigation != null) { targetEntityTypeBuilder.RemoveRelationship(inverseNavigation.ForeignKey, ConfigurationSource.DataAnnotation); } return(entityTypeBuilder); } targetEntityTypeBuilder.Relationship(entityTypeBuilder, navigationPropertyInfo, inverseNavigationPropertyInfo, ConfigurationSource.DataAnnotation); return(entityTypeBuilder); }