From 0f119395178d1de01a948f2538fa08814533f44a Mon Sep 17 00:00:00 2001 From: AndriySvyryd Date: Fri, 24 Jul 2015 14:32:46 -0700 Subject: [PATCH] Add Code First API to configure the discriminator column Add API for setting annotations on internal builders that returns success status --- src/EntityFramework.Core/DbContext.cs | 5 +- .../Metadata/Builders/PropertyBuilder.cs | 8 +- .../Metadata/EntityType.cs | 42 ++--- .../Metadata/EntityTypeExtensions.cs | 17 +- .../Metadata/ForeignKeyExtensions.cs | 11 +- .../Internal/InternalEntityTypeBuilder.cs | 139 ++++++++++------ .../Internal/InternalPropertyBuilder.cs | 46 +++++ src/EntityFramework.Core/Metadata/Property.cs | 16 +- .../Properties/Strings.Designer.cs | 14 +- .../Properties/Strings.resx | 7 +- .../EntityFramework.Relational.csproj | 11 +- .../Metadata/Builders/DiscriminatorBuilder.cs | 52 ++++++ .../Builders/DiscriminatorBuilder`.cs | 27 +++ .../RelationalColumnAttributeConvention.cs | 4 +- .../RelationalTableAttributeConvention.cs | 4 +- .../Internal/RelationalAnnotationsBuilder.cs | 33 ++++ .../RelationalEntityTypeBuilderAnnotations.cs | 117 +++++++++++++ .../RelationalForeignKeyBuilderAnnotations.cs | 22 +++ .../RelationalIndexBuilderAnnotations.cs | 20 +++ ...tionalInternalMetadataBuilderExtensions.cs | 26 +-- .../RelationalKeyBuilderAnnotations.cs | 20 +++ .../RelationalModelBuilderAnnotations.cs | 18 ++ .../RelationalPropertyBuilderAnnotations.cs | 26 +++ .../Metadata/RelationalAnnotations.cs | 52 ++++++ .../Metadata/RelationalAnnotationsBase.cs | 80 --------- .../RelationalEntityTypeAnnotations.cs | 97 +++++++---- .../RelationalForeignKeyAnnotations.cs | 32 ++-- .../Metadata/RelationalIndexAnnotations.cs | 23 +-- .../Metadata/RelationalKeyAnnotations.cs | 24 +-- .../Metadata/RelationalModelAnnotations.cs | 24 ++- .../Metadata/RelationalPropertyAnnotations.cs | 57 ++++--- .../Metadata/Sequence.cs | 3 +- .../Properties/Strings.Designer.cs | 24 +++ .../Properties/Strings.resx | 9 + .../RelationalEntityTypeBuilderExtensions.cs | 47 +++++- .../Update/CommandBatchPreparer.cs | 4 +- .../EntityFramework.SqlServer.csproj | 4 + .../SqlServerIndexBuilderAnnotations.cs | 23 +++ ...ServerInternalMetadataBuilderExtensions.cs | 25 ++- .../SqlServerKeyBuilderAnnotations.cs | 23 +++ .../SqlServerModelBuilderAnnotations.cs | 27 +++ .../SqlServerPropertyBuilderAnnotations.cs | 35 ++++ .../SqlServerIdentityStrategyConvention.cs | 3 +- .../Metadata/SqlServerIndexAnnotations.cs | 13 +- .../Metadata/SqlServerKeyAnnotations.cs | 13 +- .../Metadata/SqlServerModelAnnotations.cs | 51 ++++-- .../Metadata/SqlServerPropertyAnnotations.cs | 83 ++++----- .../SqlServerEntityTypeBuilderExtensions.cs | 15 +- ...SqliteInternalMetadataBuilderExtensions.cs | 25 ++- .../InheritanceFixtureBase.cs | 73 +------- .../Metadata/ForeignKeyTest.cs | 24 ++- .../Internal/InternalPropertyBuilderTest.cs | 64 ++++++- .../ModelBuilderTest/InheritanceTestBase.cs | 2 +- .../InheritanceRelationalFixture.cs | 37 +---- ...RelationalMetadataBuilderExtensionsTest.cs | 157 ++++++++++++++++-- .../RelationalBuilderExtensionsTest.cs | 145 +++++++++++++--- .../RelationalMetadataExtensionsTest.cs | 81 +++++++++ .../InheritanceSqlServerFixture.cs | 6 +- .../InheritanceSqlServerTest.cs | 6 +- ...lSqlServerMetadataBuilderExtensionsTest.cs | 36 ++-- .../SqliteMetadataModelProviderTest.cs | 6 +- .../InheritanceSqliteFixture.cs | 5 +- .../InheritanceSqliteTest.cs | 6 +- ...rnalSqliteMetadataBuilderExtensionsTest.cs | 30 ++-- 64 files changed, 1575 insertions(+), 604 deletions(-) create mode 100644 src/EntityFramework.Relational/Metadata/Builders/DiscriminatorBuilder.cs create mode 100644 src/EntityFramework.Relational/Metadata/Builders/DiscriminatorBuilder`.cs create mode 100644 src/EntityFramework.Relational/Metadata/Internal/RelationalAnnotationsBuilder.cs create mode 100644 src/EntityFramework.Relational/Metadata/Internal/RelationalEntityTypeBuilderAnnotations.cs create mode 100644 src/EntityFramework.Relational/Metadata/Internal/RelationalForeignKeyBuilderAnnotations.cs create mode 100644 src/EntityFramework.Relational/Metadata/Internal/RelationalIndexBuilderAnnotations.cs create mode 100644 src/EntityFramework.Relational/Metadata/Internal/RelationalKeyBuilderAnnotations.cs create mode 100644 src/EntityFramework.Relational/Metadata/Internal/RelationalModelBuilderAnnotations.cs create mode 100644 src/EntityFramework.Relational/Metadata/Internal/RelationalPropertyBuilderAnnotations.cs create mode 100644 src/EntityFramework.Relational/Metadata/RelationalAnnotations.cs delete mode 100644 src/EntityFramework.Relational/Metadata/RelationalAnnotationsBase.cs create mode 100644 src/EntityFramework.SqlServer/Metadata/Internal/SqlServerIndexBuilderAnnotations.cs create mode 100644 src/EntityFramework.SqlServer/Metadata/Internal/SqlServerKeyBuilderAnnotations.cs create mode 100644 src/EntityFramework.SqlServer/Metadata/Internal/SqlServerModelBuilderAnnotations.cs create mode 100644 src/EntityFramework.SqlServer/Metadata/Internal/SqlServerPropertyBuilderAnnotations.cs diff --git a/src/EntityFramework.Core/DbContext.cs b/src/EntityFramework.Core/DbContext.cs index c410054971c..829c428f29b 100644 --- a/src/EntityFramework.Core/DbContext.cs +++ b/src/EntityFramework.Core/DbContext.cs @@ -273,10 +273,7 @@ protected internal virtual void OnModelCreating(ModelBuilder modelBuilder) /// The number of state entries written to the underlying database. /// [DebuggerStepThrough] - public virtual int SaveChanges() - { - return SaveChanges(acceptAllChangesOnSuccess: true); - } + public virtual int SaveChanges() => SaveChanges(acceptAllChangesOnSuccess: true); /// /// Saves all changes made in this context to the underlying database. diff --git a/src/EntityFramework.Core/Metadata/Builders/PropertyBuilder.cs b/src/EntityFramework.Core/Metadata/Builders/PropertyBuilder.cs index 8c07433b064..fb5c7545842 100644 --- a/src/EntityFramework.Core/Metadata/Builders/PropertyBuilder.cs +++ b/src/EntityFramework.Core/Metadata/Builders/PropertyBuilder.cs @@ -19,8 +19,6 @@ namespace Microsoft.Data.Entity.Metadata.Builders /// public class PropertyBuilder : IAccessor, IAccessor { - private readonly InternalPropertyBuilder _builder; - /// /// /// Initializes a new instance of the class to configure a given @@ -36,13 +34,13 @@ public PropertyBuilder([NotNull] InternalPropertyBuilder builder) { Check.NotNull(builder, nameof(builder)); - _builder = builder; + Builder = builder; } /// /// The internal builder being used to configure the property. /// - InternalPropertyBuilder IAccessor.Service => _builder; + InternalPropertyBuilder IAccessor.Service => Builder; /// /// The property being configured. @@ -157,6 +155,6 @@ public virtual PropertyBuilder ValueGeneratedOnAddOrUpdate() return this; } - private InternalPropertyBuilder Builder => this.GetService(); + private InternalPropertyBuilder Builder { get; } } } diff --git a/src/EntityFramework.Core/Metadata/EntityType.cs b/src/EntityFramework.Core/Metadata/EntityType.cs index 2aad1584250..cc7d0fffaf5 100644 --- a/src/EntityFramework.Core/Metadata/EntityType.cs +++ b/src/EntityFramework.Core/Metadata/EntityType.cs @@ -122,7 +122,6 @@ public virtual EntityType BaseType var baseProperties = value.Properties.Select(p => p.Name).ToArray(); var propertyCollisions = FindPropertyCollisions(baseProperties); - // ReSharper disable once PossibleMultipleEnumeration if (propertyCollisions.Any()) { throw new InvalidOperationException( @@ -134,7 +133,6 @@ public virtual EntityType BaseType var baseNavigations = value.Navigations.Select(p => p.Name).ToArray(); var navigationCollisions = FindNavigationCollisions(baseNavigations); - // ReSharper disable once PossibleMultipleEnumeration if (navigationCollisions.Any()) { throw new InvalidOperationException( @@ -167,6 +165,8 @@ private bool InheritsFrom(EntityType entityType) return false; } + public virtual EntityType RootType() => (EntityType)((IEntityType)this).RootType(); + public virtual string Name { get @@ -368,6 +368,8 @@ public virtual Key FindKey([NotNull] IReadOnlyList properties) return FindDeclaredKey(properties) ?? BaseType?.FindKey(properties); } + + public virtual IEnumerable GetDeclaredKeys() => _keys.Values; public virtual Key FindDeclaredKey([NotNull] IReadOnlyList properties) { @@ -482,6 +484,8 @@ public virtual ForeignKey FindForeignKey([NotNull] IReadOnlyList prope return FindDeclaredForeignKey(properties) ?? BaseType?.FindForeignKey(properties); } + public virtual IEnumerable GetDeclaredForeignKeys() => _foreignKeys.Values; + public virtual ForeignKey FindDeclaredForeignKey([NotNull] IReadOnlyList properties) { Check.NotEmpty(properties, nameof(properties)); @@ -504,8 +508,7 @@ private IEnumerable FindDerivedForeignKeys(IEnumerable et.GetDeclaredForeignKeys() - .Where(foreignKey => searchForeignKeys.Contains(foreignKey.Properties))) - .Cast(); + .Where(foreignKey => searchForeignKeys.Contains(foreignKey.Properties))); } private IEnumerable FindForeignKeyCollisions(IReadOnlyList[] properties) @@ -666,8 +669,8 @@ private IEnumerable FindDerivedNavigations(IEnumerable names .Cast(); } - private IEnumerable FindNavigationCollisions(string[] propertyNames) - => FindNavigations(propertyNames).Concat(FindDerivedNavigations(propertyNames)); + private IReadOnlyList FindNavigationCollisions(string[] propertyNames) + => FindNavigations(propertyNames).Concat(FindDerivedNavigations(propertyNames)).ToList(); public virtual Navigation RemoveNavigation([NotNull] Navigation navigation) { @@ -749,6 +752,8 @@ public virtual Index FindIndex([NotNull] IReadOnlyList properties) return FindDeclaredIndex(properties) ?? BaseType?.FindIndex(properties); } + public virtual IEnumerable GetDeclaredIndexes() => _indexes.Values; + public virtual Index FindDeclaredIndex([NotNull] IReadOnlyList properties) { Check.NotEmpty(properties, nameof(properties)); @@ -771,8 +776,7 @@ private IEnumerable FindDerivedIndexes(IEnumerable et.GetDeclaredIndexes() - .Where(index => searchIndexes.Contains(index.Properties))) - .Cast(); + .Where(index => searchIndexes.Contains(index.Properties))); } private IEnumerable FindIndexCollisions(IReadOnlyList[] properties) @@ -902,21 +906,13 @@ public virtual Property FindDeclaredProperty([NotNull] string propertyName) : null; } + public virtual IEnumerable GetDeclaredProperties() => _properties.Values; + private IEnumerable FindProperties(IEnumerable propertyNames) => propertyNames.Select(FindProperty).Where(p => p != null); - - private IEnumerable FindDerivedProperties(IEnumerable propertyNames) - { - var searchProperties = new HashSet(propertyNames); - - return this.GetDerivedTypes() - .SelectMany(et => et.GetDeclaredProperties() - .Where(property => searchProperties.Contains(property.Name))) - .Cast(); - } - - private IEnumerable FindPropertyCollisions(string[] propertyNames) - => FindProperties(propertyNames).Concat(FindDerivedProperties(propertyNames)); + + private IReadOnlyList FindPropertyCollisions(string[] propertyNames) + => FindProperties(propertyNames).Concat(this.FindDerivedProperties(propertyNames)).ToList(); public virtual Property RemoveProperty([NotNull] Property property) { @@ -925,9 +921,7 @@ public virtual Property RemoveProperty([NotNull] Property property) Property removedProperty; if (_properties.TryGetValue(property.Name, out removedProperty)) { - if (GetKeys().Any(k => k.Properties.Contains(property)) - || GetForeignKeys().Any(k => k.Properties.Contains(property)) - || Indexes.Any(i => i.Properties.Contains(property))) + if (property.IsInUse) { throw new InvalidOperationException(Strings.PropertyInUse(property.Name, Name)); } diff --git a/src/EntityFramework.Core/Metadata/EntityTypeExtensions.cs b/src/EntityFramework.Core/Metadata/EntityTypeExtensions.cs index 913385d154a..6941e2f6416 100644 --- a/src/EntityFramework.Core/Metadata/EntityTypeExtensions.cs +++ b/src/EntityFramework.Core/Metadata/EntityTypeExtensions.cs @@ -31,6 +31,9 @@ public static IEnumerable GetConcreteTypesInHierarchy([NotNull] thi .Concat(entityType.GetDerivedTypes()) .Where(et => !et.IsAbstract()); } + + public static IEnumerable GetConcreteTypesInHierarchy([NotNull] this EntityType entityType) + => ((IEntityType)entityType).GetConcreteTypesInHierarchy().Cast(); private static IEnumerable GetDerivedTypes(IModel model, IEntityType entityType) { @@ -162,6 +165,18 @@ public static IProperty GetProperty([NotNull] this IEntityType entityType, [NotN return property; } + public static IEnumerable FindDerivedProperties([NotNull] this IEntityType entityType, [NotNull] IEnumerable propertyNames) + { + Check.NotNull(entityType, nameof(entityType)); + Check.NotNull(propertyNames, nameof(propertyNames)); + + var searchProperties = new HashSet(propertyNames); + + return entityType.GetDerivedTypes() + .SelectMany(et => et.GetDeclaredProperties() + .Where(property => searchProperties.Contains(property.Name))); + } + [NotNull] public static INavigation GetNavigation([NotNull] this IEntityType entityType, [NotNull] string name) { @@ -191,7 +206,7 @@ public static IEnumerable GetPropertiesAndNavigations( return entityType.GetProperties().Concat(entityType.GetNavigations()); } - public static IEnumerable GetReferencingForeignKeys([NotNull] this IEntityType entityType) + public static IEnumerable FindReferencingForeignKeys([NotNull] this IEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); diff --git a/src/EntityFramework.Core/Metadata/ForeignKeyExtensions.cs b/src/EntityFramework.Core/Metadata/ForeignKeyExtensions.cs index 99e9df3daf5..fc5d8b0eb5a 100644 --- a/src/EntityFramework.Core/Metadata/ForeignKeyExtensions.cs +++ b/src/EntityFramework.Core/Metadata/ForeignKeyExtensions.cs @@ -76,6 +76,13 @@ public static bool IsSelfReferencing([NotNull] this IForeignKey foreignKey) return foreignKey.DeclaringEntityType == foreignKey.PrincipalEntityType; } + public static bool IsSelfReferencing([NotNull] this ForeignKey foreignKey) + { + Check.NotNull(foreignKey, nameof(foreignKey)); + + return foreignKey.DeclaringEntityType == foreignKey.PrincipalEntityType; + } + public static bool IsIntraHierarchical([NotNull] this IForeignKey foreignKey) { Check.NotNull(foreignKey, nameof(foreignKey)); @@ -105,7 +112,7 @@ public static INavigation FindNavigationFrom([NotNull] this IForeignKey foreignK if (foreignKey.IsIntraHierarchical()) { - throw new InvalidOperationException(Strings.IntraHierarchicalAmbiguousNavigation(entityType.Name, Property.Format(foreignKey.Properties))); + throw new InvalidOperationException(Strings.IntraHierarchicalAmbiguousNavigation(entityType.Name, Property.Format(foreignKey.Properties), foreignKey.PrincipalEntityType, foreignKey.DeclaringEntityType)); } return foreignKey.DeclaringEntityType.IsAssignableFrom(entityType) @@ -130,7 +137,7 @@ public static INavigation FindNavigationTo([NotNull] this IForeignKey foreignKey if (foreignKey.IsIntraHierarchical()) { - throw new InvalidOperationException(Strings.IntraHierarchicalAmbiguousNavigation(entityType.Name, Property.Format(foreignKey.Properties))); + throw new InvalidOperationException(Strings.IntraHierarchicalAmbiguousNavigation(entityType.Name, Property.Format(foreignKey.Properties), foreignKey.PrincipalEntityType, foreignKey.DeclaringEntityType)); } return foreignKey.DeclaringEntityType.IsAssignableFrom(entityType) diff --git a/src/EntityFramework.Core/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EntityFramework.Core/Metadata/Internal/InternalEntityTypeBuilder.cs index ebdc2e202be..078b01d6834 100644 --- a/src/EntityFramework.Core/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EntityFramework.Core/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -462,7 +462,7 @@ public virtual InternalEntityTypeBuilder BaseType([CanBeNull] EntityType baseEnt return null; } - var detachedRelationships = new HashSet(); + var detachedRelationships = new HashSet(); var baseRelationshipsToBeRemoved = new HashSet(); if (baseEntityType != null) { @@ -490,43 +490,12 @@ public virtual InternalEntityTypeBuilder BaseType([CanBeNull] EntityType baseEnt return null; } - var baseRelationshipsByTargetType = baseEntityType.GetForeignKeys() - .Concat(baseEntityType.FindReferencingForeignKeys()) - .GroupBy(f => f.ResolveOtherEntityType(baseEntityType)) - .ToLookup(g => g.Key, g => g.ToList()); - var relationshipsByTargetType = Metadata.GetForeignKeys() - .Concat(Metadata.FindReferencingForeignKeys()) - .GroupBy(f => f.ResolveOtherEntityType(Metadata)) - .ToDictionary(g => g.Key, g => g.ToList()); var relationshipsToBeRemoved = new HashSet(); - foreach (var relatedEntityType in relationshipsByTargetType.Keys) - { - foreach (var baseRelationships in baseRelationshipsByTargetType[relatedEntityType]) - { - foreach (var foreignKey in relationshipsByTargetType[relatedEntityType]) - { - var navigation = foreignKey.FindNavigationFrom(Metadata); - foreach (var baseRelationship in baseRelationships.Where(r => - (navigation != null && r.FindNavigationFrom(baseEntityType)?.Name == navigation.Name) - || PropertyListComparer.Instance.Equals(r.Properties, foreignKey.Properties))) - { - if (baseRelationship.FindNavigationTo(baseEntityType) == null - && foreignKey.FindNavigationTo(Metadata) != null) - { - baseRelationshipsToBeRemoved.Add(baseRelationship); - } - else - { - relationshipsToBeRemoved.Add(foreignKey); - } - } - } - } - } + FindConflictingRelationships(baseEntityType, baseRelationshipsToBeRemoved, relationshipsToBeRemoved, whereDependent: true); + FindConflictingRelationships(baseEntityType, baseRelationshipsToBeRemoved, relationshipsToBeRemoved, whereDependent: false); if (baseRelationshipsToBeRemoved.Any(relationshipToBeRemoved => - !ModelBuilder.Entity(relationshipToBeRemoved.DeclaringEntityType.Name, ConfigurationSource.Convention) - .CanRemove(relationshipToBeRemoved, configurationSource, canOverrideSameSource: true))) + !CanRemove(relationshipToBeRemoved, configurationSource, canOverrideSameSource: true))) { Debug.Assert(configurationSource != ConfigurationSource.Explicit); @@ -545,8 +514,7 @@ public virtual InternalEntityTypeBuilder BaseType([CanBeNull] EntityType baseEnt foreach (var relationshipToBeRemoved in baseRelationshipsToBeRemoved) { - var removedConfigurationSource = ModelBuilder.Entity(relationshipToBeRemoved.DeclaringEntityType.Name, ConfigurationSource.Convention) - .RemoveRelationship(relationshipToBeRemoved, configurationSource); + var removedConfigurationSource = RemoveRelationship(relationshipToBeRemoved, configurationSource); Debug.Assert(removedConfigurationSource.HasValue); } @@ -560,7 +528,7 @@ public virtual InternalEntityTypeBuilder BaseType([CanBeNull] EntityType baseEnt { foreach (var referencingForeignKey in ModelBuilder.Metadata.FindReferencingForeignKeys(key).ToList()) { - detachedRelationships.Add(DetachRelationship(referencingForeignKey, configurationSource)); + detachedRelationships.Add(DetachRelationship(referencingForeignKey)); } } @@ -568,7 +536,7 @@ public virtual InternalEntityTypeBuilder BaseType([CanBeNull] EntityType baseEnt { foreach (var relationship in duplicatedProperty.FindContainingForeignKeys(Metadata).ToList()) { - detachedRelationships.Add(DetachRelationship(relationship, configurationSource)); + detachedRelationships.Add(DetachRelationship(relationship)); } // TODO: Detach indexes that contain non-duplicate properties @@ -637,6 +605,58 @@ public virtual InternalEntityTypeBuilder BaseType([CanBeNull] EntityType baseEnt return this; } + private void FindConflictingRelationships( + EntityType baseEntityType, + HashSet baseRelationshipsToBeRemoved, + HashSet relationshipsToBeRemoved, + bool whereDependent) + { + var baseRelationshipsByTargetType = GroupForeignKeysByTargetType(baseEntityType, whereDependent); + var relationshipsByTargetType = GroupForeignKeysByTargetType(Metadata, whereDependent); + + foreach (var relatedEntityType in relationshipsByTargetType.Keys) + { + if (!baseRelationshipsByTargetType.ContainsKey(relatedEntityType)) + { + continue; + } + + foreach (var baseRelationship in baseRelationshipsByTargetType[relatedEntityType]) + { + foreach (var relationship in relationshipsByTargetType[relatedEntityType]) + { + if (!relationship.ConflictsWith(baseRelationship, whereDependent)) + { + continue; + } + + if (baseRelationship.NavigationTo == null + && relationship.NavigationTo != null) + { + baseRelationshipsToBeRemoved.Add(baseRelationship.ForeignKey); + } + else + { + relationshipsToBeRemoved.Add(relationship.ForeignKey); + } + } + } + } + } + + private Dictionary> GroupForeignKeysByTargetType(EntityType entityType, bool whereDependent) + { + var foreignKeys = whereDependent + ? entityType.GetForeignKeys() + : entityType.FindReferencingForeignKeys().Where(foreignKey => !foreignKey.IsSelfReferencing()); + return foreignKeys + .GroupBy(foreignKey => whereDependent ? foreignKey.PrincipalEntityType : foreignKey.DeclaringEntityType) + .ToDictionary(g => g.Key, g => g.Select(foreignKey => + new RelationshipSnapshot(foreignKey, + whereDependent ? foreignKey.DependentToPrincipal : foreignKey.PrincipalToDependent, + whereDependent ? foreignKey.PrincipalToDependent : foreignKey.DependentToPrincipal)).ToList()); + } + private ConfigurationSource? RemoveProperty(Property property, ConfigurationSource configurationSource, bool canOverrideSameSource = true) { var removedConfigurationSource = _propertyBuilders.Remove(property, configurationSource, canOverrideSameSource); @@ -652,12 +672,12 @@ public virtual InternalEntityTypeBuilder BaseType([CanBeNull] EntityType baseEnt } var detachedRelationships = property.FindContainingForeignKeys(Metadata).ToList() - .Select(foreignKey => DetachRelationship(foreignKey, configurationSource)).ToList(); + .Select(DetachRelationship).ToList(); foreach (var key in Metadata.GetKeys().Where(i => i.Properties.Contains(property)).ToList()) { detachedRelationships.AddRange(ModelBuilder.Metadata.FindReferencingForeignKeys(key).ToList() - .Select(foreignKey => DetachRelationship(foreignKey, configurationSource))); + .Select(DetachRelationship)); var removed = RemoveKey(key, configurationSource); Debug.Assert(removed.HasValue); } @@ -708,19 +728,15 @@ private InternalRelationshipBuilder ForeignKey(EntityType principalType, IReadOn : Relationship(principalType, Metadata, null, null, configurationSource, strictPrincipal: false) ?.ForeignKey(dependentProperties, configurationSource); - private RelationshipSnapshot DetachRelationship([NotNull] ForeignKey foreignKey, ConfigurationSource configurationSource) + private RelationshipBuilderSnapshot DetachRelationship([NotNull] ForeignKey foreignKey) { var navigationToPrincipalName = foreignKey.DependentToPrincipal?.Name; var navigationToDependentName = foreignKey.PrincipalToDependent?.Name; var relationship = Relationship(foreignKey, true, ConfigurationSource.Convention); - var relationshipConfigurationSource = RemoveRelationship(foreignKey, configurationSource); + var relationshipConfigurationSource = RemoveRelationship(foreignKey, ConfigurationSource.Explicit); + Debug.Assert(relationshipConfigurationSource != null); - if (relationshipConfigurationSource == null) - { - return null; - } - - return new RelationshipSnapshot(relationship, navigationToPrincipalName, navigationToDependentName, relationshipConfigurationSource.Value); + return new RelationshipBuilderSnapshot(relationship, navigationToPrincipalName, navigationToDependentName, relationshipConfigurationSource.Value); } private bool RemoveRelationships(ConfigurationSource configurationSource, params ForeignKey[] foreignKeys) @@ -1360,7 +1376,8 @@ private Property CreateUniqueProperty(string baseName, Type propertyType, Intern while (true) { var name = baseName + (++index > 0 ? index.ToString() : ""); - if (entityTypeBuilder.Metadata.FindProperty(name) != null) + if (entityTypeBuilder.Metadata.FindProperty(name) != null + || entityTypeBuilder.Metadata.FindDerivedProperties(new[] { name }).Any()) { continue; } @@ -1461,9 +1478,27 @@ public virtual IReadOnlyList GetOrCreateProperties([CanBeNull] IEnumer return list; } - private class RelationshipSnapshot + private struct RelationshipSnapshot + { + public readonly ForeignKey ForeignKey; + public readonly Navigation NavigationFrom; + public readonly Navigation NavigationTo; + + public RelationshipSnapshot(ForeignKey foreignKey, Navigation navigationFrom, Navigation navigationTo) + { + ForeignKey = foreignKey; + NavigationFrom = navigationFrom; + NavigationTo = navigationTo; + } + + public bool ConflictsWith(RelationshipSnapshot baseRelationship, bool whereDependent) => + (NavigationFrom != null && baseRelationship.NavigationFrom?.Name == NavigationFrom.Name) + || (whereDependent && PropertyListComparer.Instance.Equals(baseRelationship.ForeignKey.Properties, ForeignKey.Properties)); + } + + private class RelationshipBuilderSnapshot { - public RelationshipSnapshot( + public RelationshipBuilderSnapshot( InternalRelationshipBuilder relationship, string navigationToPrincipalName, string navigationToDependentName, diff --git a/src/EntityFramework.Core/Metadata/Internal/InternalPropertyBuilder.cs b/src/EntityFramework.Core/Metadata/Internal/InternalPropertyBuilder.cs index 6b10b293d92..a374f310da0 100644 --- a/src/EntityFramework.Core/Metadata/Internal/InternalPropertyBuilder.cs +++ b/src/EntityFramework.Core/Metadata/Internal/InternalPropertyBuilder.cs @@ -9,6 +9,8 @@ namespace Microsoft.Data.Entity.Metadata.Internal public class InternalPropertyBuilder : InternalMetadataItemBuilder { private ConfigurationSource? _clrTypeConfigurationSource; + private ConfigurationSource? _isReadOnlyAfterSaveConfigurationSource; + private ConfigurationSource? _isReadOnlyBeforeSaveConfigurationSource; private ConfigurationSource? _isRequiredConfigurationSource; private ConfigurationSource? _isConcurrencyTokenConfigurationSource; private ConfigurationSource? _isShadowPropertyConfigurationSource; @@ -90,6 +92,50 @@ public virtual bool ConcurrencyToken(bool? isConcurrencyToken, ConfigurationSour return false; } + public virtual bool ReadOnlyAfterSave(bool? isReadOnlyAfterSave, ConfigurationSource configurationSource) + { + if (configurationSource.CanSet(_isReadOnlyAfterSaveConfigurationSource, Metadata.IsReadOnlyAfterSave.HasValue) + || Metadata.IsReadOnlyAfterSave == isReadOnlyAfterSave) + { + if (_isReadOnlyAfterSaveConfigurationSource == null + && Metadata.IsReadOnlyAfterSave != null) + { + _isReadOnlyAfterSaveConfigurationSource = ConfigurationSource.Explicit; + } + else + { + _isReadOnlyAfterSaveConfigurationSource = configurationSource.Max(_isReadOnlyAfterSaveConfigurationSource); + } + + Metadata.IsReadOnlyAfterSave = isReadOnlyAfterSave; + return true; + } + + return false; + } + + public virtual bool ReadOnlyBeforeSave(bool? isReadOnlyBeforeSave, ConfigurationSource configurationSource) + { + if (configurationSource.CanSet(_isReadOnlyBeforeSaveConfigurationSource, Metadata.IsReadOnlyBeforeSave.HasValue) + || Metadata.IsReadOnlyBeforeSave == isReadOnlyBeforeSave) + { + if (_isReadOnlyBeforeSaveConfigurationSource == null + && Metadata.IsReadOnlyBeforeSave != null) + { + _isReadOnlyBeforeSaveConfigurationSource = ConfigurationSource.Explicit; + } + else + { + _isReadOnlyBeforeSaveConfigurationSource = configurationSource.Max(_isReadOnlyBeforeSaveConfigurationSource); + } + + Metadata.IsReadOnlyBeforeSave = isReadOnlyBeforeSave; + return true; + } + + return false; + } + public virtual bool Shadow(bool? isShadowProperty, ConfigurationSource configurationSource) { if (configurationSource.CanSet(_isShadowPropertyConfigurationSource, Metadata.IsShadowProperty.HasValue) diff --git a/src/EntityFramework.Core/Metadata/Property.cs b/src/EntityFramework.Core/Metadata/Property.cs index 73c2b3f2a1b..85be16bf9de 100644 --- a/src/EntityFramework.Core/Metadata/Property.cs +++ b/src/EntityFramework.Core/Metadata/Property.cs @@ -37,7 +37,7 @@ public virtual Type ClrType set { Check.NotNull(value, nameof(value)); - if (value != _clrType) + if (value != ((IProperty)this).ClrType) { var foreignKey = this.FindReferencingForeignKeys().FirstOrDefault(); if (foreignKey != null) @@ -45,8 +45,8 @@ public virtual Type ClrType throw new InvalidOperationException( Strings.PropertyClrTypeCannotBeChangedWhenReferenced(Name, Format(foreignKey.Properties), foreignKey.DeclaringEntityType.Name)); } - _clrType = value; } + _clrType = value; } } @@ -218,6 +218,18 @@ public virtual int Index public virtual object SentinelValue { get; [param: CanBeNull] set; } + + public virtual bool IsInUse + { + get + { + return new[] { DeclaringEntityType }.Concat(DeclaringEntityType.GetDerivedTypes()).Any(entityType => + entityType.GetDeclaredKeys().Any(k => k.Properties.Contains(this)) + || entityType.GetDeclaredForeignKeys().Any(k => k.Properties.Contains(this)) + || entityType.GetDeclaredIndexes().Any(i => i.Properties.Contains(this))); + } + } + private bool? GetFlag(PropertyFlags flag) => (_setFlags & flag) != 0 ? (_flags & flag) != 0 : (bool?)null; private bool GetRequiredFlag(PropertyFlags flag) => (_flags & flag) != 0; diff --git a/src/EntityFramework.Core/Properties/Strings.Designer.cs b/src/EntityFramework.Core/Properties/Strings.Designer.cs index 55d47f30c94..d9b5e5cae5d 100644 --- a/src/EntityFramework.Core/Properties/Strings.Designer.cs +++ b/src/EntityFramework.Core/Properties/Strings.Designer.cs @@ -1021,11 +1021,11 @@ public static string NavigationStillOnEntityType([CanBeNull] object navigation, } /// - /// The navigation property corresponding to '{entityType}' cannot be determined because the specified foreign key {foreignKey} references an entity type that it is in the same hierarchy as the entity type that it is declared on. + /// The navigation property corresponding to '{entityType}' cannot be determined because the principal entity type for foreign key {foreignKey} is '{principalEntityType}' and it is in the same hierarchy as the dependent entity type '{dependentEntityType}'. /// - public static string IntraHierarchicalAmbiguousNavigation([CanBeNull] object entityType, [CanBeNull] object foreignKey) + public static string IntraHierarchicalAmbiguousNavigation([CanBeNull] object entityType, [CanBeNull] object foreignKey, [CanBeNull] object principalEntityType, [CanBeNull] object dependentEntityType) { - return string.Format(CultureInfo.CurrentCulture, GetString("IntraHierarchicalAmbiguousNavigation", "entityType", "foreignKey"), entityType, foreignKey); + return string.Format(CultureInfo.CurrentCulture, GetString("IntraHierarchicalAmbiguousNavigation", "entityType", "foreignKey", "principalEntityType", "dependentEntityType"), entityType, foreignKey, principalEntityType, dependentEntityType); } /// @@ -1132,6 +1132,14 @@ public static string SelfReferencingNavigationWithInverseProperty([CanBeNull] ob return string.Format(CultureInfo.CurrentCulture, GetString("SelfReferencingNavigationWithInverseProperty", "property", "entityType", "referencedProperty", "referencedEntityType"), property, entityType, referencedProperty, referencedEntityType); } + /// + /// The requested value cannot be changed because the specified model is read-only. + /// + public static string ImmutableModel + { + get { return GetString("ImmutableModel"); } + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/EntityFramework.Core/Properties/Strings.resx b/src/EntityFramework.Core/Properties/Strings.resx index 05d5582c109..78696302571 100644 --- a/src/EntityFramework.Core/Properties/Strings.resx +++ b/src/EntityFramework.Core/Properties/Strings.resx @@ -496,7 +496,7 @@ The navigation property '{navigation}' cannot be removed because it is still referenced from entity type '{entityType}'. - The navigation property corresponding to '{entityType}' cannot be determined because the specified foreign key {foreignKey} references an entity type that it is in the same hierarchy as the entity type that it is declared on. + The navigation property corresponding to '{entityType}' cannot be determined because the principal entity type for foreign key {foreignKey} is '{principalEntityType}' and it is in the same hierarchy as the dependent entity type '{dependentEntityType}'. The type '{entityType}' cannot have base type '{baseType}' because both types include the navigations: {navigations}. @@ -537,4 +537,7 @@ A relationship cannot be established from property '{property}' on type '{entityType}' to property '{referencedProperty}' on type '{referencedEntityType}'. Check the values in the InversePropertyAttribute to ensure relationship definitions are unique and reference from one navigation property to its corresponding inverse navigation property. - + + The requested value cannot be changed because the specified model is read-only. + + \ No newline at end of file diff --git a/src/EntityFramework.Relational/EntityFramework.Relational.csproj b/src/EntityFramework.Relational/EntityFramework.Relational.csproj index 414cb0ceace..aedf56a76d6 100644 --- a/src/EntityFramework.Relational/EntityFramework.Relational.csproj +++ b/src/EntityFramework.Relational/EntityFramework.Relational.csproj @@ -59,12 +59,21 @@ + + + + + + + - + + + diff --git a/src/EntityFramework.Relational/Metadata/Builders/DiscriminatorBuilder.cs b/src/EntityFramework.Relational/Metadata/Builders/DiscriminatorBuilder.cs new file mode 100644 index 00000000000..d52d3b34f79 --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/Builders/DiscriminatorBuilder.cs @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using JetBrains.Annotations; +using Microsoft.Data.Entity.Metadata.Internal; +using Microsoft.Data.Entity.Relational.Internal; + +namespace Microsoft.Data.Entity.Metadata.Builders +{ + public class DiscriminatorBuilder + { + public DiscriminatorBuilder([NotNull] RelationalEntityTypeBuilderAnnotations annotationsBuilder) + { + AnnotationsBuilder = annotationsBuilder; + } + + protected RelationalEntityTypeBuilderAnnotations AnnotationsBuilder { get; } + + public virtual DiscriminatorBuilder HasValue([NotNull] Type entityType, [CanBeNull] object value) + { + var entityTypeBuilder = AnnotationsBuilder.EntityTypeBuilder.ModelBuilder.Entity(entityType, ConfigurationSource.Convention); + return HasValue(entityTypeBuilder, value); + } + + public virtual DiscriminatorBuilder HasValue([NotNull] string entityTypeName, [CanBeNull] object value) + { + var entityTypeBuilder = AnnotationsBuilder.EntityTypeBuilder.ModelBuilder.Entity(entityTypeName, ConfigurationSource.Convention); + return HasValue(entityTypeBuilder, value); + } + + private DiscriminatorBuilder HasValue([NotNull] InternalEntityTypeBuilder entityTypeBuilder, [CanBeNull] object value) + { + var rootEntityTypeBuilder = AnnotationsBuilder.EntityTypeBuilder; + if (!rootEntityTypeBuilder.Metadata.IsAssignableFrom(entityTypeBuilder.Metadata) + && entityTypeBuilder.BaseType(rootEntityTypeBuilder.Metadata, AnnotationsBuilder.Annotations.ConfigurationSource) == null) + { + throw new InvalidOperationException(Strings.DiscriminatorEntityTypeNotDerived( + entityTypeBuilder.Metadata.DisplayName(), + rootEntityTypeBuilder.Metadata.DisplayName())); + } + + var annotationsBuilder = rootEntityTypeBuilder == entityTypeBuilder ? + AnnotationsBuilder + : new RelationalEntityTypeBuilderAnnotations( + entityTypeBuilder, + AnnotationsBuilder.Annotations.ConfigurationSource, + AnnotationsBuilder.Annotations.ProviderPrefix); + return annotationsBuilder.DiscriminatorValue(value) ? this : null; + } + } +} diff --git a/src/EntityFramework.Relational/Metadata/Builders/DiscriminatorBuilder`.cs b/src/EntityFramework.Relational/Metadata/Builders/DiscriminatorBuilder`.cs new file mode 100644 index 00000000000..7214c35a215 --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/Builders/DiscriminatorBuilder`.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using JetBrains.Annotations; +using Microsoft.Data.Entity.Utilities; + +namespace Microsoft.Data.Entity.Metadata.Builders +{ + public class DiscriminatorBuilder + { + public DiscriminatorBuilder([NotNull] DiscriminatorBuilder builder) + { + Check.NotNull(builder, nameof(builder)); + + Builder = builder; + } + + private DiscriminatorBuilder Builder { get; } + + public virtual DiscriminatorBuilder HasValue([NotNull] Type entityType, [CanBeNull] TDiscriminator value) + => Builder.HasValue(entityType, value); + + public virtual DiscriminatorBuilder HasValue([NotNull] string entityTypeName, [CanBeNull] TDiscriminator value) + => Builder.HasValue(entityTypeName, value); + } +} diff --git a/src/EntityFramework.Relational/Metadata/Conventions/Internal/RelationalColumnAttributeConvention.cs b/src/EntityFramework.Relational/Metadata/Conventions/Internal/RelationalColumnAttributeConvention.cs index 066fc889ed1..5782d235e40 100644 --- a/src/EntityFramework.Relational/Metadata/Conventions/Internal/RelationalColumnAttributeConvention.cs +++ b/src/EntityFramework.Relational/Metadata/Conventions/Internal/RelationalColumnAttributeConvention.cs @@ -17,12 +17,12 @@ public override InternalPropertyBuilder Apply(InternalPropertyBuilder propertyBu if (!string.IsNullOrWhiteSpace(attribute.Name)) { - propertyBuilder.Relational(ConfigurationSource.DataAnnotation).ColumnName = attribute.Name; + propertyBuilder.Relational(ConfigurationSource.DataAnnotation).ColumnName(attribute.Name); } if (!string.IsNullOrWhiteSpace(attribute.TypeName)) { - propertyBuilder.Relational(ConfigurationSource.DataAnnotation).ColumnType = attribute.TypeName; + propertyBuilder.Relational(ConfigurationSource.DataAnnotation).ColumnType(attribute.TypeName); } return propertyBuilder; diff --git a/src/EntityFramework.Relational/Metadata/Conventions/Internal/RelationalTableAttributeConvention.cs b/src/EntityFramework.Relational/Metadata/Conventions/Internal/RelationalTableAttributeConvention.cs index 18a8a69fba1..d4552eaf3b6 100644 --- a/src/EntityFramework.Relational/Metadata/Conventions/Internal/RelationalTableAttributeConvention.cs +++ b/src/EntityFramework.Relational/Metadata/Conventions/Internal/RelationalTableAttributeConvention.cs @@ -16,12 +16,12 @@ public override InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entity if (!string.IsNullOrWhiteSpace(attribute.Schema)) { - entityTypeBuilder.Relational(ConfigurationSource.DataAnnotation).Schema = attribute.Schema; + entityTypeBuilder.Relational(ConfigurationSource.DataAnnotation).ToTable(attribute.Name, attribute.Schema); } if (!string.IsNullOrWhiteSpace(attribute.Name)) { - entityTypeBuilder.Relational(ConfigurationSource.DataAnnotation).TableName = attribute.Name; + entityTypeBuilder.Relational(ConfigurationSource.DataAnnotation).ToTable(attribute.Name); } return entityTypeBuilder; diff --git a/src/EntityFramework.Relational/Metadata/Internal/RelationalAnnotationsBuilder.cs b/src/EntityFramework.Relational/Metadata/Internal/RelationalAnnotationsBuilder.cs new file mode 100644 index 00000000000..82d92558a65 --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/Internal/RelationalAnnotationsBuilder.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; +using Microsoft.Data.Entity.Utilities; + +namespace Microsoft.Data.Entity.Metadata.Internal +{ + public class RelationalAnnotationsBuilder : RelationalAnnotations + { + public RelationalAnnotationsBuilder( + [NotNull] InternalMetadataBuilder internalBuilder, + ConfigurationSource configurationSource, + [CanBeNull] string providerPrefix) + : base(internalBuilder.Metadata, providerPrefix) + { + Check.NotNull(internalBuilder, nameof(internalBuilder)); + + EntityTypeBuilder = internalBuilder; + ConfigurationSource = configurationSource; + } + + public virtual ConfigurationSource ConfigurationSource { get; } + + public virtual InternalMetadataBuilder EntityTypeBuilder { get; } + + public override bool SetAnnotation(string annotationName, object value) + { + var fullName = (ProviderPrefix ?? RelationalAnnotationNames.Prefix) + annotationName; + return EntityTypeBuilder.Annotation(fullName, value, ConfigurationSource); + } + } +} diff --git a/src/EntityFramework.Relational/Metadata/Internal/RelationalEntityTypeBuilderAnnotations.cs b/src/EntityFramework.Relational/Metadata/Internal/RelationalEntityTypeBuilderAnnotations.cs new file mode 100644 index 00000000000..3f1f99661f8 --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/Internal/RelationalEntityTypeBuilderAnnotations.cs @@ -0,0 +1,117 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.Data.Entity.Metadata.Builders; +using Microsoft.Data.Entity.Utilities; + +namespace Microsoft.Data.Entity.Metadata.Internal +{ + public class RelationalEntityTypeBuilderAnnotations : RelationalEntityTypeAnnotations + { + public RelationalEntityTypeBuilderAnnotations( + [NotNull] InternalEntityTypeBuilder internalBuilder, + ConfigurationSource configurationSource, + [CanBeNull] string providerPrefix) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, providerPrefix)) + { + } + + public new virtual RelationalAnnotationsBuilder Annotations => (RelationalAnnotationsBuilder)base.Annotations; + + public virtual InternalEntityTypeBuilder EntityTypeBuilder => (InternalEntityTypeBuilder)Annotations.EntityTypeBuilder; + + public virtual bool ToTable([CanBeNull] string name) + { + Check.NullButNotEmpty(name, nameof(name)); + + return SetTableName(name); + } + + public virtual bool ToTable([CanBeNull] string name, [CanBeNull] string schema) + { + Check.NullButNotEmpty(name, nameof(name)); + Check.NullButNotEmpty(schema, nameof(schema)); + + var originalTable = TableName; + if (!SetTableName(name)) + { + return false; + } + + if (!SetSchema(schema)) + { + SetTableName(originalTable); + return false; + } + + return true; + } + + public virtual DiscriminatorBuilder Discriminator() => DiscriminatorBuilder(null); + + public virtual DiscriminatorBuilder Discriminator([NotNull] string name, [NotNull] Type discriminatorType) + => DiscriminatorBuilder(b => b.Property(name, discriminatorType, Annotations.ConfigurationSource)); + + public virtual DiscriminatorBuilder Discriminator([NotNull] PropertyInfo propertyInfo) + => DiscriminatorBuilder(b => b.Property(propertyInfo, Annotations.ConfigurationSource)); + + private DiscriminatorBuilder DiscriminatorBuilder( + [CanBeNull] Func createProperty) + { + EnsureCanSetDiscriminator(); + + var discriminatorProperty = DiscriminatorProperty; + if (discriminatorProperty != null + && createProperty != null) + { + if (!SetDiscriminatorProperty(null)) + { + return null; + } + } + + InternalPropertyBuilder propertyBuilder; + if (createProperty != null) + { + propertyBuilder = createProperty(EntityTypeBuilder); + } + else if (discriminatorProperty == null) + { + propertyBuilder = EntityTypeBuilder.Property(GetDefaultDiscriminatorName(), ConfigurationSource.Convention); + } + else + { + propertyBuilder = EntityTypeBuilder.Property(discriminatorProperty.Name, ConfigurationSource.Convention); + } + + if (propertyBuilder == null) + { + if (discriminatorProperty != null + && createProperty != null) + { + SetDiscriminatorProperty(discriminatorProperty); + } + return null; + } + + var discriminatorSet = SetDiscriminatorProperty(propertyBuilder.Metadata); + Debug.Assert(discriminatorSet); + + var configurationSource = (Annotations).ConfigurationSource; + propertyBuilder.Required(true, configurationSource); + //propertyBuilder.ReadOnlyBeforeSave(true, configurationSource);// #2132 + propertyBuilder.ReadOnlyAfterSave(true, configurationSource); + propertyBuilder.UseValueGenerator(true, configurationSource); + + return new DiscriminatorBuilder(this); + } + + public new virtual bool DiscriminatorValue([CanBeNull] object value) => SetDiscriminatorValue(value); + + protected virtual string GetDefaultDiscriminatorName() => "Discriminator"; + } +} diff --git a/src/EntityFramework.Relational/Metadata/Internal/RelationalForeignKeyBuilderAnnotations.cs b/src/EntityFramework.Relational/Metadata/Internal/RelationalForeignKeyBuilderAnnotations.cs new file mode 100644 index 00000000000..016d6ad3e13 --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/Internal/RelationalForeignKeyBuilderAnnotations.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; + +namespace Microsoft.Data.Entity.Metadata.Internal +{ + public class RelationalForeignKeyBuilderAnnotations : RelationalForeignKeyAnnotations + { + public RelationalForeignKeyBuilderAnnotations( + [NotNull] InternalRelationshipBuilder internalBuilder, + ConfigurationSource configurationSource, + [CanBeNull] string providerPrefix) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, providerPrefix)) + { + } + + protected virtual new RelationalAnnotationsBuilder Annotations => (RelationalAnnotationsBuilder)base.Annotations; + + public new virtual bool Name([CanBeNull] string value) => SetName(value); + } +} diff --git a/src/EntityFramework.Relational/Metadata/Internal/RelationalIndexBuilderAnnotations.cs b/src/EntityFramework.Relational/Metadata/Internal/RelationalIndexBuilderAnnotations.cs new file mode 100644 index 00000000000..0681c58e420 --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/Internal/RelationalIndexBuilderAnnotations.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; + +namespace Microsoft.Data.Entity.Metadata.Internal +{ + public class RelationalIndexBuilderAnnotations : RelationalIndexAnnotations + { + public RelationalIndexBuilderAnnotations( + [NotNull] InternalIndexBuilder internalBuilder, + ConfigurationSource configurationSource, + [CanBeNull] string providerPrefix) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, providerPrefix)) + { + } + + public new virtual bool Name([CanBeNull] string value) => SetName(value); + } +} diff --git a/src/EntityFramework.Relational/Metadata/Internal/RelationalInternalMetadataBuilderExtensions.cs b/src/EntityFramework.Relational/Metadata/Internal/RelationalInternalMetadataBuilderExtensions.cs index ed03c112c6c..fa41a17c1ab 100644 --- a/src/EntityFramework.Relational/Metadata/Internal/RelationalInternalMetadataBuilderExtensions.cs +++ b/src/EntityFramework.Relational/Metadata/Internal/RelationalInternalMetadataBuilderExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using JetBrains.Annotations; @@ -7,34 +7,34 @@ namespace Microsoft.Data.Entity.Metadata.Internal { public static class RelationalInternalMetadataBuilderExtensions { - public static RelationalModelAnnotations Relational( + public static RelationalModelBuilderAnnotations Relational( [NotNull] this InternalModelBuilder builder, ConfigurationSource configurationSource) - => new RelationalModelAnnotations(builder, configurationSource, null); + => new RelationalModelBuilderAnnotations(builder, configurationSource, null); - public static RelationalPropertyAnnotations Relational( + public static RelationalPropertyBuilderAnnotations Relational( [NotNull] this InternalPropertyBuilder builder, ConfigurationSource configurationSource) - => new RelationalPropertyAnnotations(builder, configurationSource, null); + => new RelationalPropertyBuilderAnnotations(builder, configurationSource, null); - public static RelationalEntityTypeAnnotations Relational( + public static RelationalEntityTypeBuilderAnnotations Relational( [NotNull] this InternalEntityTypeBuilder builder, ConfigurationSource configurationSource) - => new RelationalEntityTypeAnnotations(builder, configurationSource, null); + => new RelationalEntityTypeBuilderAnnotations(builder, configurationSource, null); - public static RelationalKeyAnnotations Relational( + public static RelationalKeyBuilderAnnotations Relational( [NotNull] this InternalKeyBuilder builder, ConfigurationSource configurationSource) - => new RelationalKeyAnnotations(builder, configurationSource, null); + => new RelationalKeyBuilderAnnotations(builder, configurationSource, null); - public static RelationalIndexAnnotations Relational( + public static RelationalIndexBuilderAnnotations Relational( [NotNull] this InternalIndexBuilder builder, ConfigurationSource configurationSource) - => new RelationalIndexAnnotations(builder, configurationSource, null); + => new RelationalIndexBuilderAnnotations(builder, configurationSource, null); - public static RelationalForeignKeyAnnotations Relational( + public static RelationalForeignKeyBuilderAnnotations Relational( [NotNull] this InternalRelationshipBuilder builder, ConfigurationSource configurationSource) - => new RelationalForeignKeyAnnotations(builder, configurationSource, null); + => new RelationalForeignKeyBuilderAnnotations(builder, configurationSource, null); } } diff --git a/src/EntityFramework.Relational/Metadata/Internal/RelationalKeyBuilderAnnotations.cs b/src/EntityFramework.Relational/Metadata/Internal/RelationalKeyBuilderAnnotations.cs new file mode 100644 index 00000000000..7a8248c18a2 --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/Internal/RelationalKeyBuilderAnnotations.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; + +namespace Microsoft.Data.Entity.Metadata.Internal +{ + public class RelationalKeyBuilderAnnotations : RelationalKeyAnnotations + { + public RelationalKeyBuilderAnnotations( + [NotNull] InternalKeyBuilder internalBuilder, + ConfigurationSource configurationSource, + [CanBeNull] string providerPrefix) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, providerPrefix)) + { + } + + public new virtual bool Name([CanBeNull] string value) => SetName(value); + } +} diff --git a/src/EntityFramework.Relational/Metadata/Internal/RelationalModelBuilderAnnotations.cs b/src/EntityFramework.Relational/Metadata/Internal/RelationalModelBuilderAnnotations.cs new file mode 100644 index 00000000000..0d920fab280 --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/Internal/RelationalModelBuilderAnnotations.cs @@ -0,0 +1,18 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; + +namespace Microsoft.Data.Entity.Metadata.Internal +{ + public class RelationalModelBuilderAnnotations : RelationalModelAnnotations + { + public RelationalModelBuilderAnnotations( + [NotNull] InternalModelBuilder internalBuilder, + ConfigurationSource configurationSource, + [CanBeNull] string providerPrefix) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, providerPrefix)) + { + } + } +} diff --git a/src/EntityFramework.Relational/Metadata/Internal/RelationalPropertyBuilderAnnotations.cs b/src/EntityFramework.Relational/Metadata/Internal/RelationalPropertyBuilderAnnotations.cs new file mode 100644 index 00000000000..fa166cf060b --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/Internal/RelationalPropertyBuilderAnnotations.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; + +namespace Microsoft.Data.Entity.Metadata.Internal +{ + public class RelationalPropertyBuilderAnnotations : RelationalPropertyAnnotations + { + public RelationalPropertyBuilderAnnotations( + [NotNull] InternalPropertyBuilder internalBuilder, + ConfigurationSource configurationSource, + [CanBeNull] string providerPrefix) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, providerPrefix)) + { + } + + public new virtual bool ColumnName([CanBeNull] string value) => SetColumnName(value); + + public new virtual bool ColumnType([CanBeNull] string value) => SetColumnType(value); + + public new virtual bool GeneratedValueSql([CanBeNull] string value) => SetGeneratedValueSql(value); + + public new virtual bool DefaultValue([CanBeNull] object value) => SetDefaultValue(value); + } +} diff --git a/src/EntityFramework.Relational/Metadata/RelationalAnnotations.cs b/src/EntityFramework.Relational/Metadata/RelationalAnnotations.cs new file mode 100644 index 00000000000..c236c0a4ba5 --- /dev/null +++ b/src/EntityFramework.Relational/Metadata/RelationalAnnotations.cs @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using JetBrains.Annotations; +using Microsoft.Data.Entity.Infrastructure; +using Microsoft.Data.Entity.Internal; +using Microsoft.Data.Entity.Utilities; + +namespace Microsoft.Data.Entity.Metadata +{ + public class RelationalAnnotations + { + public RelationalAnnotations( + [NotNull] IAnnotatable metadata, + [CanBeNull] string providerPrefix) + { + Check.NotNull(metadata, nameof(metadata)); + Check.NullButNotEmpty(providerPrefix, nameof(providerPrefix)); + + Metadata = metadata; + ProviderPrefix = providerPrefix; + } + + public virtual IAnnotatable Metadata { get; } + + public virtual string ProviderPrefix { get; } + + public virtual object GetAnnotation([NotNull] string annotationName) + { + Check.NotEmpty(annotationName, nameof(annotationName)); + + return (ProviderPrefix == null ? null : Metadata[ProviderPrefix + annotationName]) + ?? Metadata[RelationalAnnotationNames.Prefix + annotationName]; + } + + public virtual bool SetAnnotation([NotNull] string annotationName, [CanBeNull] object value) + { + Check.NotEmpty(annotationName, nameof(annotationName)); + + var annotatable = Metadata as Annotatable; + if (annotatable == null) + { + throw new InvalidOperationException(Strings.ImmutableModel); + } + + var fullName = (ProviderPrefix ?? RelationalAnnotationNames.Prefix) + annotationName; + annotatable[fullName] = value; + return true; + } + } +} diff --git a/src/EntityFramework.Relational/Metadata/RelationalAnnotationsBase.cs b/src/EntityFramework.Relational/Metadata/RelationalAnnotationsBase.cs deleted file mode 100644 index d238e612975..00000000000 --- a/src/EntityFramework.Relational/Metadata/RelationalAnnotationsBase.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using JetBrains.Annotations; -using Microsoft.Data.Entity.Infrastructure; -using Microsoft.Data.Entity.Metadata.Internal; -using Microsoft.Data.Entity.Utilities; - -namespace Microsoft.Data.Entity.Metadata -{ - public abstract class RelationalAnnotationsBase - { - private readonly ConfigurationSource _configurationSource; - - private readonly InternalMetadataBuilder _internalBuilder; - - protected RelationalAnnotationsBase( - [NotNull] IAnnotatable metadata, - [CanBeNull] string providerPrefix) - { - Check.NotNull(metadata, nameof(metadata)); - Check.NullButNotEmpty(providerPrefix, nameof(providerPrefix)); - - Metadata = metadata; - _configurationSource = ConfigurationSource.Explicit; - ProviderPrefix = providerPrefix; - } - - protected RelationalAnnotationsBase( - [NotNull] InternalMetadataBuilder internalBuilder, - ConfigurationSource configurationSource, - [CanBeNull] string providerPrefix) - { - Check.NotNull(internalBuilder, nameof(internalBuilder)); - Check.NullButNotEmpty(providerPrefix, nameof(providerPrefix)); - - Metadata = internalBuilder.Metadata; - _internalBuilder = internalBuilder; - _configurationSource = configurationSource; - ProviderPrefix = providerPrefix; - } - - protected virtual IAnnotatable Metadata { get; } - - protected virtual string ProviderPrefix { get; } - - protected virtual object GetAnnotation([NotNull] string annotationName) - { - Check.NotEmpty(annotationName, nameof(annotationName)); - - return (ProviderPrefix == null ? null : Metadata[ProviderPrefix + annotationName]) - ?? Metadata[RelationalAnnotationNames.Prefix + annotationName]; - } - - protected virtual void SetAnnotation([NotNull] string annotationName, [CanBeNull] object value) - { - Check.NotEmpty(annotationName, nameof(annotationName)); - - var fullName = (ProviderPrefix ?? RelationalAnnotationNames.Prefix) + annotationName; - - if (_configurationSource != ConfigurationSource.Explicit) - { - _internalBuilder.Annotation(fullName, value, _configurationSource); - } - else - { - ((Annotatable)Metadata)[fullName] = value; - } - } - - protected virtual object GetAnnotation([NotNull] IAnnotatable metadata, [NotNull] string annotationName) - { - Check.NotNull(metadata, nameof(metadata)); - Check.NotEmpty(annotationName, nameof(annotationName)); - - return (ProviderPrefix == null ? null : metadata[ProviderPrefix + annotationName]) - ?? metadata[RelationalAnnotationNames.Prefix + annotationName]; - } - } -} diff --git a/src/EntityFramework.Relational/Metadata/RelationalEntityTypeAnnotations.cs b/src/EntityFramework.Relational/Metadata/RelationalEntityTypeAnnotations.cs index c00206c2d34..f734170c073 100644 --- a/src/EntityFramework.Relational/Metadata/RelationalEntityTypeAnnotations.cs +++ b/src/EntityFramework.Relational/Metadata/RelationalEntityTypeAnnotations.cs @@ -3,87 +3,116 @@ using System; using JetBrains.Annotations; -using Microsoft.Data.Entity.Metadata.Internal; using Microsoft.Data.Entity.Relational.Internal; using Microsoft.Data.Entity.Utilities; namespace Microsoft.Data.Entity.Metadata { - public class RelationalEntityTypeAnnotations : RelationalAnnotationsBase, IRelationalEntityTypeAnnotations + public class RelationalEntityTypeAnnotations : IRelationalEntityTypeAnnotations { public RelationalEntityTypeAnnotations([NotNull] IEntityType entityType, [CanBeNull] string providerPrefix) - : base(entityType, providerPrefix) + : this(new RelationalAnnotations(entityType, providerPrefix)) { } - public RelationalEntityTypeAnnotations( - [NotNull] InternalEntityTypeBuilder internalBuilder, - ConfigurationSource configurationSource, - [CanBeNull] string providerPrefix) - : base(internalBuilder, configurationSource, providerPrefix) + protected RelationalEntityTypeAnnotations([NotNull] RelationalAnnotations annotations) { + Annotations = annotations; } - protected virtual IEntityType EntityType => (IEntityType)Metadata; + protected RelationalAnnotations Annotations { get; } + + protected virtual IEntityType EntityType => (IEntityType)Annotations.Metadata; public virtual string TableName { get { var rootType = EntityType.RootType(); + var rootAnnotations = new RelationalAnnotations(rootType, Annotations.ProviderPrefix); - return (string)GetAnnotation(rootType, RelationalAnnotationNames.TableName) + return (string)rootAnnotations.GetAnnotation(RelationalAnnotationNames.TableName) ?? rootType.DisplayName(); } - [param: CanBeNull] set { SetAnnotation(RelationalAnnotationNames.TableName, Check.NullButNotEmpty(value, nameof(value))); } + [param: CanBeNull] set { SetTableName(value); } } + protected virtual bool SetTableName(string value) + => Annotations.SetAnnotation(RelationalAnnotationNames.TableName, Check.NullButNotEmpty(value, nameof(value))); + public virtual string Schema { - get { return (string)GetAnnotation(EntityType.RootType(), RelationalAnnotationNames.Schema); } - [param: CanBeNull] set { SetAnnotation(RelationalAnnotationNames.Schema, Check.NullButNotEmpty(value, nameof(value))); } + get + { + var rootAnnotations = new RelationalAnnotations(EntityType.RootType(), Annotations.ProviderPrefix); + return (string)rootAnnotations.GetAnnotation(RelationalAnnotationNames.Schema); + } + [param: CanBeNull] set { SetSchema(value); } } + protected virtual bool SetSchema(string value) + => Annotations.SetAnnotation(RelationalAnnotationNames.Schema, Check.NullButNotEmpty(value, nameof(value))); + public virtual IProperty DiscriminatorProperty { get { var rootType = EntityType.RootType(); - - var propertyName = (string)GetAnnotation(rootType, RelationalAnnotationNames.DiscriminatorProperty); + var rootAnnotations = new RelationalAnnotations(rootType, Annotations.ProviderPrefix); + var propertyName = (string)rootAnnotations.GetAnnotation(RelationalAnnotationNames.DiscriminatorProperty); return propertyName == null ? null : rootType.GetProperty(propertyName); } - [param: CanBeNull] - set + [param: CanBeNull] set { SetDiscriminatorProperty(value); } + } + + protected virtual bool SetDiscriminatorProperty([CanBeNull] IProperty value) + { + if (value != null) { - if (value != null) + EnsureCanSetDiscriminator(); + + if (value.DeclaringEntityType != EntityType) { - if (EntityType != EntityType.RootType()) - { - throw new InvalidOperationException( - Strings.DiscriminatorPropertyMustBeOnRoot(EntityType)); - } - - if (value.DeclaringEntityType != EntityType) - { - throw new InvalidOperationException( - Strings.DiscriminatorPropertyNotFound(value, EntityType)); - } + throw new InvalidOperationException( + Strings.DiscriminatorPropertyNotFound(value, EntityType)); } + } - SetAnnotation(RelationalAnnotationNames.DiscriminatorProperty, value?.Name); + return Annotations.SetAnnotation(RelationalAnnotationNames.DiscriminatorProperty, value?.Name); + } + + protected virtual void EnsureCanSetDiscriminator() + { + if (EntityType != EntityType.RootType()) + { + throw new InvalidOperationException( + Strings.DiscriminatorPropertyMustBeOnRoot(EntityType)); } } public virtual object DiscriminatorValue { - get + get { return Annotations.GetAnnotation(RelationalAnnotationNames.DiscriminatorValue); } + [param: CanBeNull] set { SetDiscriminatorValue(value); } + } + + protected virtual bool SetDiscriminatorValue(object value) + { + if (DiscriminatorProperty == null) + { + throw new InvalidOperationException( + Strings.NoDiscriminator(EntityType.DisplayName(), EntityType.RootType().DisplayName())); + } + + // ReSharper disable once UseMethodIsInstanceOfType + if (value != null && !DiscriminatorProperty.ClrType.IsAssignableFrom(value.GetType())) { - return GetAnnotation(RelationalAnnotationNames.DiscriminatorValue) - ?? EntityType.DisplayName(); + throw new InvalidOperationException(Strings.DiscriminitatorValueIncompatible( + value, DiscriminatorProperty.Name, DiscriminatorProperty.ClrType)); } - [param: CanBeNull] set { SetAnnotation(RelationalAnnotationNames.DiscriminatorValue, value); } + + return Annotations.SetAnnotation(RelationalAnnotationNames.DiscriminatorValue, value); } } } diff --git a/src/EntityFramework.Relational/Metadata/RelationalForeignKeyAnnotations.cs b/src/EntityFramework.Relational/Metadata/RelationalForeignKeyAnnotations.cs index 9e7588c98db..f950292eac8 100644 --- a/src/EntityFramework.Relational/Metadata/RelationalForeignKeyAnnotations.cs +++ b/src/EntityFramework.Relational/Metadata/RelationalForeignKeyAnnotations.cs @@ -3,40 +3,38 @@ using System.Linq; using JetBrains.Annotations; -using Microsoft.Data.Entity.Metadata.Internal; using Microsoft.Data.Entity.Utilities; namespace Microsoft.Data.Entity.Metadata { - public class RelationalForeignKeyAnnotations : RelationalAnnotationsBase, IRelationalForeignKeyAnnotations + public class RelationalForeignKeyAnnotations : IRelationalForeignKeyAnnotations { public RelationalForeignKeyAnnotations([NotNull] IForeignKey foreignKey, [CanBeNull] string providerPrefix) - : base(foreignKey, providerPrefix) + : this(new RelationalAnnotations(foreignKey, providerPrefix)) { } - public RelationalForeignKeyAnnotations( - [NotNull] InternalRelationshipBuilder internalBuilder, - ConfigurationSource configurationSource, - [CanBeNull] string providerPrefix) - : base(internalBuilder, configurationSource, providerPrefix) + protected RelationalForeignKeyAnnotations([NotNull] RelationalAnnotations annotations) { + Annotations = annotations; } - protected virtual IForeignKey ForeignKey => (IForeignKey)Metadata; + protected virtual RelationalAnnotations Annotations { get; } + + protected virtual IForeignKey ForeignKey => (IForeignKey)Annotations.Metadata; public virtual string Name { - get { return (string)GetAnnotation(RelationalAnnotationNames.Name) ?? GetDefaultName(); } - [param: CanBeNull] set { SetAnnotation(RelationalAnnotationNames.Name, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(RelationalAnnotationNames.Name) ?? GetDefaultName(); } + [param: CanBeNull] set { SetName(value); } } + protected virtual bool SetName([CanBeNull] string value) + => Annotations.SetAnnotation(RelationalAnnotationNames.Name, Check.NullButNotEmpty(value, nameof(value))); + protected virtual string GetDefaultName() - => "FK_" + - ForeignKey.DeclaringEntityType.DisplayName() + - "_" + - ForeignKey.PrincipalEntityType.DisplayName() + - "_" + - string.Join("_", ForeignKey.Properties.Select(p => p.Name)); + => "FK_" + ForeignKey.DeclaringEntityType.DisplayName() + + "_" + ForeignKey.PrincipalEntityType.DisplayName() + + "_" + string.Join("_", ForeignKey.Properties.Select(p => p.Name)); } } diff --git a/src/EntityFramework.Relational/Metadata/RelationalIndexAnnotations.cs b/src/EntityFramework.Relational/Metadata/RelationalIndexAnnotations.cs index 57c027a9550..e057b7206e5 100644 --- a/src/EntityFramework.Relational/Metadata/RelationalIndexAnnotations.cs +++ b/src/EntityFramework.Relational/Metadata/RelationalIndexAnnotations.cs @@ -3,34 +3,35 @@ using System.Linq; using JetBrains.Annotations; -using Microsoft.Data.Entity.Metadata.Internal; using Microsoft.Data.Entity.Utilities; namespace Microsoft.Data.Entity.Metadata { - public class RelationalIndexAnnotations : RelationalAnnotationsBase, IRelationalIndexAnnotations + public class RelationalIndexAnnotations : IRelationalIndexAnnotations { public RelationalIndexAnnotations([NotNull] IIndex key, [CanBeNull] string providerPrefix) - : base(key, providerPrefix) + : this(new RelationalAnnotations(key, providerPrefix)) { } - public RelationalIndexAnnotations( - [NotNull] InternalIndexBuilder internalBuilder, - ConfigurationSource configurationSource, - [CanBeNull] string providerPrefix) - : base(internalBuilder, configurationSource, providerPrefix) + protected RelationalIndexAnnotations([NotNull] RelationalAnnotations annotations) { + Annotations = annotations; } - protected virtual IIndex Index => (IIndex)Metadata; + protected RelationalAnnotations Annotations { get; } + + protected virtual IIndex Index => (IIndex)Annotations.Metadata; public virtual string Name { - get { return (string)GetAnnotation(RelationalAnnotationNames.Name) ?? GetDefaultName(); } - [param: CanBeNull] set { SetAnnotation(RelationalAnnotationNames.Name, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(RelationalAnnotationNames.Name) ?? GetDefaultName(); } + [param: CanBeNull] set { SetName(value); } } + protected virtual bool SetName([CanBeNull] string value) + => Annotations.SetAnnotation(RelationalAnnotationNames.Name, Check.NullButNotEmpty(value, nameof(value))); + protected virtual string GetDefaultName() => "IX_" + Index.DeclaringEntityType.DisplayName() + diff --git a/src/EntityFramework.Relational/Metadata/RelationalKeyAnnotations.cs b/src/EntityFramework.Relational/Metadata/RelationalKeyAnnotations.cs index 408824ba111..9e24de08177 100644 --- a/src/EntityFramework.Relational/Metadata/RelationalKeyAnnotations.cs +++ b/src/EntityFramework.Relational/Metadata/RelationalKeyAnnotations.cs @@ -9,29 +9,31 @@ namespace Microsoft.Data.Entity.Metadata { - public class RelationalKeyAnnotations : RelationalAnnotationsBase, IRelationalKeyAnnotations + public class RelationalKeyAnnotations : IRelationalKeyAnnotations { public RelationalKeyAnnotations([NotNull] IKey key, [CanBeNull] string providerPrefix) - : base(key, providerPrefix) + : this(new RelationalAnnotations(key, providerPrefix)) { } - - public RelationalKeyAnnotations( - [NotNull] InternalKeyBuilder internalBuilder, - ConfigurationSource configurationSource, - [CanBeNull] string providerPrefix) - : base(internalBuilder, configurationSource, providerPrefix) + + protected RelationalKeyAnnotations([NotNull] RelationalAnnotations annotations) { + Annotations = annotations; } - protected virtual IKey Key => (IKey)Metadata; + protected RelationalAnnotations Annotations { get; } + + protected virtual IKey Key => (IKey)Annotations.Metadata; public virtual string Name { - get { return (string)GetAnnotation(RelationalAnnotationNames.Name) ?? GetDefaultName(); } - [param: CanBeNull] set { SetAnnotation(RelationalAnnotationNames.Name, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(RelationalAnnotationNames.Name) ?? GetDefaultName(); } + [param: CanBeNull] set { SetName(value); } } + protected virtual bool SetName([CanBeNull] string value) + => Annotations.SetAnnotation(RelationalAnnotationNames.Name, Check.NullButNotEmpty(value, nameof(value))); + protected virtual string GetDefaultName() { var builder = new StringBuilder(); diff --git a/src/EntityFramework.Relational/Metadata/RelationalModelAnnotations.cs b/src/EntityFramework.Relational/Metadata/RelationalModelAnnotations.cs index 36b634d9265..a8d90000718 100644 --- a/src/EntityFramework.Relational/Metadata/RelationalModelAnnotations.cs +++ b/src/EntityFramework.Relational/Metadata/RelationalModelAnnotations.cs @@ -5,34 +5,32 @@ using System.Collections.Immutable; using System.Linq; using JetBrains.Annotations; -using Microsoft.Data.Entity.Metadata.Internal; using Microsoft.Data.Entity.Utilities; namespace Microsoft.Data.Entity.Metadata { - public class RelationalModelAnnotations : RelationalAnnotationsBase, IRelationalModelAnnotations + public class RelationalModelAnnotations : IRelationalModelAnnotations { public RelationalModelAnnotations([NotNull] IModel model, [CanBeNull] string providerPrefix) - : base(model, providerPrefix) + : this(new RelationalAnnotations(model, providerPrefix)) { } - public RelationalModelAnnotations( - [NotNull] InternalModelBuilder internalBuilder, - ConfigurationSource configurationSource, - [CanBeNull] string providerPrefix) - : base(internalBuilder, configurationSource, providerPrefix) + protected RelationalModelAnnotations([NotNull] RelationalAnnotations annotations) { + Annotations = annotations; } - protected virtual IModel Model => (IModel)Metadata; + protected RelationalAnnotations Annotations { get; } + + protected virtual IModel Model => (IModel)Annotations.Metadata; public virtual IReadOnlyList Sequences { get { - var providerSequences = ProviderPrefix != null - ? Sequence.GetSequences(Model, ProviderPrefix).ToList() + var providerSequences = Annotations.ProviderPrefix != null + ? Sequence.GetSequences(Model, Annotations.ProviderPrefix).ToList() : (IList)ImmutableList.Empty; return Sequence.GetSequences(Model, RelationalAnnotationNames.Prefix) @@ -43,13 +41,13 @@ public virtual IReadOnlyList Sequences } public virtual ISequence FindSequence(string name, string schema = null) - => (ProviderPrefix == null ? null : Sequence.FindSequence(Model, ProviderPrefix, name, schema)) + => (Annotations.ProviderPrefix == null ? null : Sequence.FindSequence(Model, Annotations.ProviderPrefix, name, schema)) ?? Sequence.FindSequence(Model, RelationalAnnotationNames.Prefix, name, schema); public virtual Sequence GetOrAddSequence([CanBeNull] string name, [CanBeNull] string schema = null) => new Sequence( (Model)Model, - ProviderPrefix ?? RelationalAnnotationNames.Prefix, + Annotations.ProviderPrefix ?? RelationalAnnotationNames.Prefix, Check.NotEmpty(name, nameof(name)), Check.NullButNotEmpty(schema, nameof(schema))); } diff --git a/src/EntityFramework.Relational/Metadata/RelationalPropertyAnnotations.cs b/src/EntityFramework.Relational/Metadata/RelationalPropertyAnnotations.cs index 1e2e1a37379..f82516d42ea 100644 --- a/src/EntityFramework.Relational/Metadata/RelationalPropertyAnnotations.cs +++ b/src/EntityFramework.Relational/Metadata/RelationalPropertyAnnotations.cs @@ -2,61 +2,70 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using JetBrains.Annotations; -using Microsoft.Data.Entity.Metadata.Internal; using Microsoft.Data.Entity.Utilities; namespace Microsoft.Data.Entity.Metadata { - public class RelationalPropertyAnnotations : RelationalAnnotationsBase, IRelationalPropertyAnnotations + public class RelationalPropertyAnnotations : IRelationalPropertyAnnotations { public RelationalPropertyAnnotations([NotNull] IProperty property, [CanBeNull] string providerPrefix) - : base(property, providerPrefix) + : this(new RelationalAnnotations(property, providerPrefix)) { } - - public RelationalPropertyAnnotations( - [NotNull] InternalPropertyBuilder internalBuilder, - ConfigurationSource configurationSource, - [CanBeNull] string providerPrefix) - : base(internalBuilder, configurationSource, providerPrefix) + + protected RelationalPropertyAnnotations([NotNull] RelationalAnnotations annotations) { + Annotations = annotations; } - protected virtual IProperty Property => (IProperty)Metadata; + protected RelationalAnnotations Annotations { get; } + + protected virtual IProperty Property => (IProperty)Annotations.Metadata; public virtual string ColumnName { - get { return (string)GetAnnotation(RelationalAnnotationNames.ColumnName) ?? Property.Name; } - [param: CanBeNull] set { SetAnnotation(RelationalAnnotationNames.ColumnName, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(RelationalAnnotationNames.ColumnName) ?? Property.Name; } + [param: CanBeNull] set { SetColumnName(value); } } + protected bool SetColumnName([CanBeNull] string value) + => Annotations.SetAnnotation(RelationalAnnotationNames.ColumnName, Check.NullButNotEmpty(value, nameof(value))); + public virtual string ColumnType { - get { return (string)GetAnnotation(RelationalAnnotationNames.ColumnType); } - [param: CanBeNull] set { SetAnnotation(RelationalAnnotationNames.ColumnType, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(RelationalAnnotationNames.ColumnType); } + [param: CanBeNull] set { SetColumnType(value); } } + protected bool SetColumnType([CanBeNull] string value) + => Annotations.SetAnnotation(RelationalAnnotationNames.ColumnType, Check.NullButNotEmpty(value, nameof(value))); + public virtual string GeneratedValueSql { - get { return (string)GetAnnotation(RelationalAnnotationNames.GeneratedValueSql); } - [param: CanBeNull] set { SetAnnotation(RelationalAnnotationNames.GeneratedValueSql, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(RelationalAnnotationNames.GeneratedValueSql); } + [param: CanBeNull] set { SetGeneratedValueSql(value); } } + protected bool SetGeneratedValueSql([CanBeNull] string value) + => Annotations.SetAnnotation(RelationalAnnotationNames.GeneratedValueSql, Check.NullButNotEmpty(value, nameof(value))); + public virtual object DefaultValue { get { return new TypedAnnotation( - (string)GetAnnotation(RelationalAnnotationNames.DefaultValueType), - (string)GetAnnotation(RelationalAnnotationNames.DefaultValue)).Value; + (string)Annotations.GetAnnotation(RelationalAnnotationNames.DefaultValueType), + (string)Annotations.GetAnnotation(RelationalAnnotationNames.DefaultValue)).Value; } [param: CanBeNull] - set - { - var typedAnnotation = new TypedAnnotation(value); - SetAnnotation(RelationalAnnotationNames.DefaultValueType, typedAnnotation.TypeString); - SetAnnotation(RelationalAnnotationNames.DefaultValue, typedAnnotation.ValueString); - } + set { SetDefaultValue(value); } + } + + protected bool SetDefaultValue([CanBeNull] object value) + { + var typedAnnotation = new TypedAnnotation(value); + return Annotations.SetAnnotation(RelationalAnnotationNames.DefaultValueType, typedAnnotation.TypeString) && + Annotations.SetAnnotation(RelationalAnnotationNames.DefaultValue, typedAnnotation.ValueString); } } } diff --git a/src/EntityFramework.Relational/Metadata/Sequence.cs b/src/EntityFramework.Relational/Metadata/Sequence.cs index 1b0348228a5..b23d7d0b8a7 100644 --- a/src/EntityFramework.Relational/Metadata/Sequence.cs +++ b/src/EntityFramework.Relational/Metadata/Sequence.cs @@ -44,8 +44,7 @@ public Sequence( }); } } - - + private Sequence(IModel model, string annotationName) { _model = model; diff --git a/src/EntityFramework.Relational/Properties/Strings.Designer.cs b/src/EntityFramework.Relational/Properties/Strings.Designer.cs index bcdba02f82e..d9dc4636947 100644 --- a/src/EntityFramework.Relational/Properties/Strings.Designer.cs +++ b/src/EntityFramework.Relational/Properties/Strings.Designer.cs @@ -396,6 +396,30 @@ public static string InvalidCreateScript get { return GetString("InvalidCreateScript"); } } + /// + /// Cannot configure the discriminator value for entity type '{entityType}' because it doesn't derive from '{rootEntityType}'. + /// + public static string DiscriminatorEntityTypeNotDerived([CanBeNull] object entityType, [CanBeNull] object rootEntityType) + { + return string.Format(CultureInfo.CurrentCulture, GetString("DiscriminatorEntityTypeNotDerived", "entityType", "rootEntityType"), entityType, rootEntityType); + } + + /// + /// Cannot set discriminator value '{value}' for discriminator property '{discriminator}' because it is not assignable to property of type '{discriminatorType}'. + /// + public static string DiscriminitatorValueIncompatible([CanBeNull] object value, [CanBeNull] object discriminator, [CanBeNull] object discriminatorType) + { + return string.Format(CultureInfo.CurrentCulture, GetString("DiscriminitatorValueIncompatible", "value", "discriminator", "discriminatorType"), value, discriminator, discriminatorType); + } + + /// + /// Cannot set discriminator value for entity type '{entityType}' because the root entity type '{rootEntityType}' doesn't have a discriminator property set. + /// + public static string NoDiscriminator([CanBeNull] object entityType, [CanBeNull] object rootEntityType) + { + return string.Format(CultureInfo.CurrentCulture, GetString("NoDiscriminator", "entityType", "rootEntityType"), entityType, rootEntityType); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/EntityFramework.Relational/Properties/Strings.resx b/src/EntityFramework.Relational/Properties/Strings.resx index 13e811b460d..44289439024 100644 --- a/src/EntityFramework.Relational/Properties/Strings.resx +++ b/src/EntityFramework.Relational/Properties/Strings.resx @@ -262,4 +262,13 @@ GetCreateScript only handles operations in a single batch without transaction suppression. For more advanced scenarios, override HistoryRepository.GetCreateScript in your provider. + + Cannot configure the discriminator value for entity type '{entityType}' because it doesn't derive from '{rootEntityType}'. + + + Cannot set discriminator value '{value}' for discriminator property '{discriminator}' because it is not assignable to property of type '{discriminatorType}'. + + + Cannot set discriminator value for entity type '{entityType}' because the root entity type '{rootEntityType}' doesn't have a discriminator property set. + \ No newline at end of file diff --git a/src/EntityFramework.Relational/RelationalEntityTypeBuilderExtensions.cs b/src/EntityFramework.Relational/RelationalEntityTypeBuilderExtensions.cs index c569e1d4596..bc933fed563 100644 --- a/src/EntityFramework.Relational/RelationalEntityTypeBuilderExtensions.cs +++ b/src/EntityFramework.Relational/RelationalEntityTypeBuilderExtensions.cs @@ -1,8 +1,13 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Linq.Expressions; using JetBrains.Annotations; +using Microsoft.Data.Entity.Infrastructure; +using Microsoft.Data.Entity.Metadata; using Microsoft.Data.Entity.Metadata.Builders; +using Microsoft.Data.Entity.Metadata.Internal; using Microsoft.Data.Entity.Utilities; // ReSharper disable once CheckNamespace @@ -18,7 +23,9 @@ public static EntityTypeBuilder ToTable( Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); Check.NullButNotEmpty(name, nameof(name)); - entityTypeBuilder.Metadata.Relational().TableName = name; + ((IAccessor)entityTypeBuilder).GetService() + .Relational(ConfigurationSource.Explicit) + .ToTable(name); return entityTypeBuilder; } @@ -38,8 +45,9 @@ public static EntityTypeBuilder ToTable( Check.NullButNotEmpty(name, nameof(name)); Check.NullButNotEmpty(schema, nameof(schema)); - entityTypeBuilder.Metadata.Relational().TableName = name; - entityTypeBuilder.Metadata.Relational().Schema = schema; + ((IAccessor)entityTypeBuilder).GetService() + .Relational(ConfigurationSource.Explicit) + .ToTable(name, schema); return entityTypeBuilder; } @@ -50,5 +58,38 @@ public static EntityTypeBuilder ToTable( [CanBeNull] string schema) where TEntity : class => (EntityTypeBuilder)ToTable((EntityTypeBuilder)entityTypeBuilder, name, schema); + + public static DiscriminatorBuilder Discriminator([NotNull] this EntityTypeBuilder entityTypeBuilder) + { + Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); + + return ((IAccessor)entityTypeBuilder).GetService() + .Relational(ConfigurationSource.Explicit).Discriminator(); + } + + public static DiscriminatorBuilder Discriminator( + [NotNull] this EntityTypeBuilder entityTypeBuilder, + [NotNull] string name, + [NotNull] Type discriminatorType) + { + Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); + Check.NotEmpty(name, nameof(name)); + Check.NotNull(discriminatorType, nameof(discriminatorType)); + + return ((IAccessor)entityTypeBuilder).GetService() + .Relational(ConfigurationSource.Explicit).Discriminator(name, discriminatorType); + } + + public static DiscriminatorBuilder Discriminator( + [NotNull] this EntityTypeBuilder entityTypeBuilder, + [NotNull] Expression> propertyExpression) + where TEntity : class + { + Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); + Check.NotNull(propertyExpression, nameof(propertyExpression)); + + return new DiscriminatorBuilder(((IAccessor)entityTypeBuilder).GetService() + .Relational(ConfigurationSource.Explicit).Discriminator(propertyExpression.GetPropertyAccess())); + } } } diff --git a/src/EntityFramework.Relational/Update/CommandBatchPreparer.cs b/src/EntityFramework.Relational/Update/CommandBatchPreparer.cs index 5c1f027fa83..6f99cba39b4 100644 --- a/src/EntityFramework.Relational/Update/CommandBatchPreparer.cs +++ b/src/EntityFramework.Relational/Update/CommandBatchPreparer.cs @@ -120,7 +120,7 @@ private Dictionary> CreateKeyValuePredecesso foreach (var entry in command.Entries) { // TODO: Perf: Consider only adding foreign keys defined on entity types involved in a modification - foreach (var foreignKey in entry.EntityType.GetReferencingForeignKeys()) + foreach (var foreignKey in entry.EntityType.FindReferencingForeignKeys()) { var candidateKeyValueColumnModifications = command.ColumnModifications.Where(cm => @@ -221,7 +221,7 @@ private void AddForeignKeyEdges( { foreach (var entry in command.Entries) { - foreach (var foreignKey in entry.EntityType.GetReferencingForeignKeys()) + foreach (var foreignKey in entry.EntityType.FindReferencingForeignKeys()) { var principalKeyValue = CreatePrincipalKeyValue(entry.OriginalValues, foreignKey, ValueType.Original); diff --git a/src/EntityFramework.SqlServer/EntityFramework.SqlServer.csproj b/src/EntityFramework.SqlServer/EntityFramework.SqlServer.csproj index 5cf9218a727..34b4d203441 100644 --- a/src/EntityFramework.SqlServer/EntityFramework.SqlServer.csproj +++ b/src/EntityFramework.SqlServer/EntityFramework.SqlServer.csproj @@ -45,7 +45,11 @@ + + + + diff --git a/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerIndexBuilderAnnotations.cs b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerIndexBuilderAnnotations.cs new file mode 100644 index 00000000000..0edda2a42fb --- /dev/null +++ b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerIndexBuilderAnnotations.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; +using Microsoft.Data.Entity.Metadata; +using Microsoft.Data.Entity.Metadata.Internal; + +namespace Microsoft.Data.Entity.SqlServer.Metadata.Internal +{ + public class SqlServerIndexBuilderAnnotations : SqlServerIndexAnnotations + { + public SqlServerIndexBuilderAnnotations( + [NotNull] InternalIndexBuilder internalBuilder, + ConfigurationSource configurationSource) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, SqlServerAnnotationNames.Prefix)) + { + } + + public new virtual bool Name([CanBeNull] string value) => SetName(value); + + public virtual bool Clustered(bool value) => SetIsClustered(value); + } +} diff --git a/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerInternalMetadataBuilderExtensions.cs b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerInternalMetadataBuilderExtensions.cs index 34b94637ee9..f4dd36fb392 100644 --- a/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerInternalMetadataBuilderExtensions.cs +++ b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerInternalMetadataBuilderExtensions.cs @@ -2,41 +2,40 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using JetBrains.Annotations; -using Microsoft.Data.Entity.Metadata; using Microsoft.Data.Entity.Metadata.Internal; namespace Microsoft.Data.Entity.SqlServer.Metadata.Internal { public static class SqlServerInternalMetadataBuilderExtensions { - public static SqlServerModelAnnotations SqlServer( + public static SqlServerModelBuilderAnnotations SqlServer( [NotNull] this InternalModelBuilder builder, ConfigurationSource configurationSource) - => new SqlServerModelAnnotations(builder, configurationSource); + => new SqlServerModelBuilderAnnotations(builder, configurationSource); - public static SqlServerPropertyAnnotations SqlServer( + public static SqlServerPropertyBuilderAnnotations SqlServer( [NotNull] this InternalPropertyBuilder builder, ConfigurationSource configurationSource) - => new SqlServerPropertyAnnotations(builder, configurationSource); + => new SqlServerPropertyBuilderAnnotations(builder, configurationSource); - public static RelationalEntityTypeAnnotations SqlServer( + public static RelationalEntityTypeBuilderAnnotations SqlServer( [NotNull] this InternalEntityTypeBuilder builder, ConfigurationSource configurationSource) - => new RelationalEntityTypeAnnotations(builder, configurationSource, SqlServerAnnotationNames.Prefix); + => new RelationalEntityTypeBuilderAnnotations(builder, configurationSource, SqlServerAnnotationNames.Prefix); - public static SqlServerKeyAnnotations SqlServer( + public static SqlServerKeyBuilderAnnotations SqlServer( [NotNull] this InternalKeyBuilder builder, ConfigurationSource configurationSource) - => new SqlServerKeyAnnotations(builder, configurationSource); + => new SqlServerKeyBuilderAnnotations(builder, configurationSource); - public static SqlServerIndexAnnotations SqlServer( + public static SqlServerIndexBuilderAnnotations SqlServer( [NotNull] this InternalIndexBuilder builder, ConfigurationSource configurationSource) - => new SqlServerIndexAnnotations(builder, configurationSource); + => new SqlServerIndexBuilderAnnotations(builder, configurationSource); - public static RelationalForeignKeyAnnotations SqlServer( + public static RelationalForeignKeyBuilderAnnotations SqlServer( [NotNull] this InternalRelationshipBuilder builder, ConfigurationSource configurationSource) - => new RelationalForeignKeyAnnotations(builder, configurationSource, SqlServerAnnotationNames.Prefix); + => new RelationalForeignKeyBuilderAnnotations(builder, configurationSource, SqlServerAnnotationNames.Prefix); } } diff --git a/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerKeyBuilderAnnotations.cs b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerKeyBuilderAnnotations.cs new file mode 100644 index 00000000000..8fa59044415 --- /dev/null +++ b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerKeyBuilderAnnotations.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; +using Microsoft.Data.Entity.Metadata; +using Microsoft.Data.Entity.Metadata.Internal; + +namespace Microsoft.Data.Entity.SqlServer.Metadata.Internal +{ + public class SqlServerKeyBuilderAnnotations : SqlServerKeyAnnotations + { + public SqlServerKeyBuilderAnnotations( + [NotNull] InternalKeyBuilder internalBuilder, + ConfigurationSource configurationSource) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, SqlServerAnnotationNames.Prefix)) + { + } + + public new virtual bool Name([CanBeNull] string value) => SetName(value); + + public virtual bool Clustered(bool value) => SetIsClustered(value); + } +} diff --git a/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerModelBuilderAnnotations.cs b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerModelBuilderAnnotations.cs new file mode 100644 index 00000000000..034ed810d6b --- /dev/null +++ b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerModelBuilderAnnotations.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; +using Microsoft.Data.Entity.Metadata; +using Microsoft.Data.Entity.Metadata.Internal; + +namespace Microsoft.Data.Entity.SqlServer.Metadata.Internal +{ + public class SqlServerModelBuilderAnnotations : SqlServerModelAnnotations + { + public SqlServerModelBuilderAnnotations( + [NotNull] InternalModelBuilder internalBuilder, + ConfigurationSource configurationSource) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, SqlServerAnnotationNames.Prefix)) + { + } + + public new virtual bool HiLoSequenceName([CanBeNull] string value) => SetHiLoSequenceName(value); + + public new virtual bool HiLoSequenceSchema([CanBeNull] string value) => SetHiLoSequenceSchema(value); + + public new virtual bool HiLoSequencePoolSize(int? value) => SetHiLoSequencePoolSize(value); + + public new virtual bool IdentityStrategy(SqlServerIdentityStrategy? value) => SetIdentityStrategy(value); + } +} diff --git a/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerPropertyBuilderAnnotations.cs b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerPropertyBuilderAnnotations.cs new file mode 100644 index 00000000000..3fadabf0280 --- /dev/null +++ b/src/EntityFramework.SqlServer/Metadata/Internal/SqlServerPropertyBuilderAnnotations.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; +using Microsoft.Data.Entity.Metadata; +using Microsoft.Data.Entity.Metadata.Internal; + +namespace Microsoft.Data.Entity.SqlServer.Metadata.Internal +{ + public class SqlServerPropertyBuilderAnnotations : SqlServerPropertyAnnotations + { + public SqlServerPropertyBuilderAnnotations( + [NotNull] InternalPropertyBuilder internalBuilder, + ConfigurationSource configurationSource) + : base(new RelationalAnnotationsBuilder(internalBuilder, configurationSource, SqlServerAnnotationNames.Prefix)) + { + } + + public new virtual bool ColumnName([CanBeNull] string value) => SetColumnName(value); + + public new virtual bool ColumnType([CanBeNull] string value) => SetColumnType(value); + + public new virtual bool GeneratedValueSql([CanBeNull] string value) => SetGeneratedValueSql(value); + + public new virtual bool DefaultValue([CanBeNull] object value) => SetDefaultValue(value); + + public new virtual bool HiLoSequenceName([CanBeNull] string value) => SetHiLoSequenceName(value); + + public new virtual bool HiLoSequenceSchema([CanBeNull] string value) => SetHiLoSequenceSchema(value); + + public new virtual bool HiLoSequencePoolSize(int? value) => SetHiLoSequencePoolSize(value); + + public new virtual bool IdentityStrategy(SqlServerIdentityStrategy? value) => SetIdentityStrategy(value); + } +} diff --git a/src/EntityFramework.SqlServer/Metadata/ModelConventions/SqlServerIdentityStrategyConvention.cs b/src/EntityFramework.SqlServer/Metadata/ModelConventions/SqlServerIdentityStrategyConvention.cs index c3547d99c2f..83fe6c927bf 100644 --- a/src/EntityFramework.SqlServer/Metadata/ModelConventions/SqlServerIdentityStrategyConvention.cs +++ b/src/EntityFramework.SqlServer/Metadata/ModelConventions/SqlServerIdentityStrategyConvention.cs @@ -11,8 +11,7 @@ public class SqlServerIdentityStrategyConvention : IModelConvention { public virtual InternalModelBuilder Apply(InternalModelBuilder modelBuilder) { - modelBuilder.SqlServer(ConfigurationSource.Convention).IdentityStrategy - = SqlServerIdentityStrategy.IdentityColumn; + modelBuilder.SqlServer(ConfigurationSource.Convention).IdentityStrategy(SqlServerIdentityStrategy.IdentityColumn); return modelBuilder; } diff --git a/src/EntityFramework.SqlServer/Metadata/SqlServerIndexAnnotations.cs b/src/EntityFramework.SqlServer/Metadata/SqlServerIndexAnnotations.cs index 50d3449ca74..693f376fe90 100644 --- a/src/EntityFramework.SqlServer/Metadata/SqlServerIndexAnnotations.cs +++ b/src/EntityFramework.SqlServer/Metadata/SqlServerIndexAnnotations.cs @@ -3,7 +3,6 @@ using JetBrains.Annotations; using Microsoft.Data.Entity.Metadata; -using Microsoft.Data.Entity.Metadata.Internal; namespace Microsoft.Data.Entity.SqlServer.Metadata { @@ -14,17 +13,17 @@ public SqlServerIndexAnnotations([NotNull] IIndex index) { } - public SqlServerIndexAnnotations( - [NotNull] InternalIndexBuilder internalBuilder, - ConfigurationSource configurationSource) - : base(internalBuilder, configurationSource, SqlServerAnnotationNames.Prefix) + protected SqlServerIndexAnnotations([NotNull] RelationalAnnotations annotations) + : base(annotations) { } public virtual bool? IsClustered { - get { return (bool?)GetAnnotation(SqlServerAnnotationNames.Clustered); } - [param: CanBeNull] set { SetAnnotation(SqlServerAnnotationNames.Clustered, value); } + get { return (bool?)Annotations.GetAnnotation(SqlServerAnnotationNames.Clustered); } + [param: CanBeNull] set { SetIsClustered(value); } } + + protected bool SetIsClustered(bool? value) => Annotations.SetAnnotation(SqlServerAnnotationNames.Clustered, value); } } diff --git a/src/EntityFramework.SqlServer/Metadata/SqlServerKeyAnnotations.cs b/src/EntityFramework.SqlServer/Metadata/SqlServerKeyAnnotations.cs index 272a0701ee4..d2bdf430aad 100644 --- a/src/EntityFramework.SqlServer/Metadata/SqlServerKeyAnnotations.cs +++ b/src/EntityFramework.SqlServer/Metadata/SqlServerKeyAnnotations.cs @@ -3,7 +3,6 @@ using JetBrains.Annotations; using Microsoft.Data.Entity.Metadata; -using Microsoft.Data.Entity.Metadata.Internal; namespace Microsoft.Data.Entity.SqlServer.Metadata { @@ -14,17 +13,17 @@ public SqlServerKeyAnnotations([NotNull] IKey key) { } - public SqlServerKeyAnnotations( - [NotNull] InternalKeyBuilder internalBuilder, - ConfigurationSource configurationSource) - : base(internalBuilder, configurationSource, SqlServerAnnotationNames.Prefix) + protected SqlServerKeyAnnotations([NotNull] RelationalAnnotations annotations) + : base(annotations) { } public virtual bool? IsClustered { - get { return (bool?)GetAnnotation(SqlServerAnnotationNames.Clustered); } - [param: CanBeNull] set { SetAnnotation(SqlServerAnnotationNames.Clustered, value); } + get { return (bool?)Annotations.GetAnnotation(SqlServerAnnotationNames.Clustered); } + [param: CanBeNull] set { SetIsClustered(value); } } + + protected bool SetIsClustered(bool? value) => Annotations.SetAnnotation(SqlServerAnnotationNames.Clustered, value); } } diff --git a/src/EntityFramework.SqlServer/Metadata/SqlServerModelAnnotations.cs b/src/EntityFramework.SqlServer/Metadata/SqlServerModelAnnotations.cs index 10f1948d2c5..fd1430c6161 100644 --- a/src/EntityFramework.SqlServer/Metadata/SqlServerModelAnnotations.cs +++ b/src/EntityFramework.SqlServer/Metadata/SqlServerModelAnnotations.cs @@ -1,9 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using JetBrains.Annotations; using Microsoft.Data.Entity.Metadata; -using Microsoft.Data.Entity.Metadata.Internal; using Microsoft.Data.Entity.Utilities; namespace Microsoft.Data.Entity.SqlServer.Metadata @@ -15,35 +15,52 @@ public SqlServerModelAnnotations([NotNull] IModel model) { } - public SqlServerModelAnnotations( - [NotNull] InternalModelBuilder internalBuilder, - ConfigurationSource configurationSource) - : base(internalBuilder, configurationSource, SqlServerAnnotationNames.Prefix) + protected SqlServerModelAnnotations([NotNull] RelationalAnnotations annotations) + : base(annotations) { } - public virtual SqlServerIdentityStrategy? IdentityStrategy - { - get { return (SqlServerIdentityStrategy?)GetAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy); } - [param: CanBeNull] set { SetAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy, value); } - } - public virtual string HiLoSequenceName { - get { return (string)GetAnnotation(SqlServerAnnotationNames.HiLoSequenceName); } - [param: CanBeNull] set { SetAnnotation(SqlServerAnnotationNames.HiLoSequenceName, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(SqlServerAnnotationNames.HiLoSequenceName); } + [param: CanBeNull] set { SetHiLoSequenceName(value); } } + protected virtual bool SetHiLoSequenceName([CanBeNull]string value) + => Annotations.SetAnnotation(SqlServerAnnotationNames.HiLoSequenceName, Check.NullButNotEmpty(value, nameof(value))); + public virtual string HiLoSequenceSchema { - get { return (string)GetAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema); } - [param: CanBeNull] set { SetAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema); } + [param: CanBeNull] set { SetHiLoSequenceSchema(value); } } + protected virtual bool SetHiLoSequenceSchema([CanBeNull]string value) + => Annotations.SetAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema, Check.NullButNotEmpty(value, nameof(value))); + public virtual int? HiLoSequencePoolSize { - get { return (int?)GetAnnotation(SqlServerAnnotationNames.HiLoSequencePoolSize); } - [param: CanBeNull] set { SetAnnotation(SqlServerAnnotationNames.HiLoSequencePoolSize, value); } + get { return (int?)Annotations.GetAnnotation(SqlServerAnnotationNames.HiLoSequencePoolSize); } + set { SetHiLoSequencePoolSize(value); } } + + protected virtual bool SetHiLoSequencePoolSize(int? value) + { + if (value <= 0) + { + throw new ArgumentOutOfRangeException(nameof(value), Entity.Internal.Strings.HiLoBadPoolSize); + } + + return Annotations.SetAnnotation(SqlServerAnnotationNames.HiLoSequencePoolSize, value); + } + + public virtual SqlServerIdentityStrategy? IdentityStrategy + { + get { return (SqlServerIdentityStrategy?)Annotations.GetAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy); } + set { SetIdentityStrategy(value); } + } + + protected virtual bool SetIdentityStrategy(SqlServerIdentityStrategy? value) + => Annotations.SetAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy, value); } } diff --git a/src/EntityFramework.SqlServer/Metadata/SqlServerPropertyAnnotations.cs b/src/EntityFramework.SqlServer/Metadata/SqlServerPropertyAnnotations.cs index dd3f98abe87..1c51a93f845 100644 --- a/src/EntityFramework.SqlServer/Metadata/SqlServerPropertyAnnotations.cs +++ b/src/EntityFramework.SqlServer/Metadata/SqlServerPropertyAnnotations.cs @@ -4,7 +4,6 @@ using System; using JetBrains.Annotations; using Microsoft.Data.Entity.Metadata; -using Microsoft.Data.Entity.Metadata.Internal; using Microsoft.Data.Entity.Utilities; namespace Microsoft.Data.Entity.SqlServer.Metadata @@ -16,37 +15,43 @@ public SqlServerPropertyAnnotations([NotNull] IProperty property) { } - public SqlServerPropertyAnnotations( - [NotNull] InternalPropertyBuilder internalBuilder, - ConfigurationSource configurationSource) - : base(internalBuilder, configurationSource, SqlServerAnnotationNames.Prefix) + protected SqlServerPropertyAnnotations([NotNull] RelationalAnnotations annotations) + : base(annotations) { } public virtual string HiLoSequenceName { - get { return (string)GetAnnotation(SqlServerAnnotationNames.HiLoSequenceName); } - [param: CanBeNull] set { SetAnnotation(SqlServerAnnotationNames.HiLoSequenceName, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(SqlServerAnnotationNames.HiLoSequenceName); } + [param: CanBeNull] set { SetHiLoSequenceName(value); } } + protected virtual bool SetHiLoSequenceName(string value) + => Annotations.SetAnnotation(SqlServerAnnotationNames.HiLoSequenceName, Check.NullButNotEmpty(value, nameof(value))); + public virtual string HiLoSequenceSchema { - get { return (string)GetAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema); } - [param: CanBeNull] set { SetAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema, Check.NullButNotEmpty(value, nameof(value))); } + get { return (string)Annotations.GetAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema); } + [param: CanBeNull] set { SetHiLoSequenceSchema(value); } } + protected virtual bool SetHiLoSequenceSchema(string value) + => Annotations.SetAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema, Check.NullButNotEmpty(value, nameof(value))); + public virtual int? HiLoSequencePoolSize { - get { return (int?)GetAnnotation(SqlServerAnnotationNames.HiLoSequencePoolSize); } - [param: CanBeNull] set - { - if (value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(value), Entity.Internal.Strings.HiLoBadPoolSize); - } + get { return (int?)Annotations.GetAnnotation(SqlServerAnnotationNames.HiLoSequencePoolSize); } + [param: CanBeNull] set { SetHiLoSequencePoolSize(value); } + } - SetAnnotation(SqlServerAnnotationNames.HiLoSequencePoolSize, value); + protected virtual bool SetHiLoSequencePoolSize(int? value) + { + if (value <= 0) + { + throw new ArgumentOutOfRangeException(nameof(value), Entity.Internal.Strings.HiLoBadPoolSize); } + + return Annotations.SetAnnotation(SqlServerAnnotationNames.HiLoSequencePoolSize, value); } public virtual SqlServerIdentityStrategy? IdentityStrategy @@ -60,36 +65,38 @@ public virtual SqlServerIdentityStrategy? IdentityStrategy return null; } - var value = (SqlServerIdentityStrategy?)GetAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy); + var value = (SqlServerIdentityStrategy?)Annotations.GetAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy); return value ?? Property.DeclaringEntityType.Model.SqlServer().IdentityStrategy; } [param: CanBeNull] - set + set { SetIdentityStrategy(value); } + } + + protected virtual bool SetIdentityStrategy(SqlServerIdentityStrategy? value) + { + if (value != null) { - if (value != null) + var propertyType = Property.ClrType; + + if (value == SqlServerIdentityStrategy.IdentityColumn + && (!propertyType.IsInteger() + || propertyType == typeof(byte) + || propertyType == typeof(byte?))) { - var propertyType = Property.ClrType; - - if (value == SqlServerIdentityStrategy.IdentityColumn - && (!propertyType.IsInteger() - || propertyType == typeof(byte) - || propertyType == typeof(byte?))) - { - throw new ArgumentException(Strings.IdentityBadType( - Property.Name, Property.DeclaringEntityType.Name, propertyType.Name)); - } - - if (value == SqlServerIdentityStrategy.SequenceHiLo - && !propertyType.IsInteger()) - { - throw new ArgumentException(Strings.SequenceBadType( - Property.Name, Property.DeclaringEntityType.Name, propertyType.Name)); - } + throw new ArgumentException(Strings.IdentityBadType( + Property.Name, Property.DeclaringEntityType.Name, propertyType.Name)); } - SetAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy, value); + if (value == SqlServerIdentityStrategy.SequenceHiLo + && !propertyType.IsInteger()) + { + throw new ArgumentException(Strings.SequenceBadType( + Property.Name, Property.DeclaringEntityType.Name, propertyType.Name)); + } } + + return Annotations.SetAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy, value); } public virtual ISequence FindHiLoSequence() diff --git a/src/EntityFramework.SqlServer/SqlServerEntityTypeBuilderExtensions.cs b/src/EntityFramework.SqlServer/SqlServerEntityTypeBuilderExtensions.cs index 44de257d34f..d6c740e3e4c 100644 --- a/src/EntityFramework.SqlServer/SqlServerEntityTypeBuilderExtensions.cs +++ b/src/EntityFramework.SqlServer/SqlServerEntityTypeBuilderExtensions.cs @@ -2,7 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using JetBrains.Annotations; +using Microsoft.Data.Entity.Infrastructure; using Microsoft.Data.Entity.Metadata.Builders; +using Microsoft.Data.Entity.Metadata.Internal; +using Microsoft.Data.Entity.SqlServer.Metadata.Internal; using Microsoft.Data.Entity.Utilities; namespace Microsoft.Data.Entity @@ -16,7 +19,9 @@ public static EntityTypeBuilder ToSqlServerTable( Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); Check.NullButNotEmpty(name, nameof(name)); - entityTypeBuilder.Metadata.SqlServer().TableName = name; + var relationalEntityTypeBuilder = ((IAccessor)entityTypeBuilder).GetService() + .SqlServer(ConfigurationSource.Explicit); + relationalEntityTypeBuilder.TableName = name; return entityTypeBuilder; } @@ -35,9 +40,11 @@ public static EntityTypeBuilder ToSqlServerTable( Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); Check.NullButNotEmpty(name, nameof(name)); Check.NullButNotEmpty(schema, nameof(schema)); - - entityTypeBuilder.Metadata.SqlServer().TableName = name; - entityTypeBuilder.Metadata.SqlServer().Schema = schema; + + var relationalEntityTypeBuilder = ((IAccessor)entityTypeBuilder).GetService() + .SqlServer(ConfigurationSource.Explicit); + relationalEntityTypeBuilder.TableName = name; + relationalEntityTypeBuilder.Schema = schema; return entityTypeBuilder; } diff --git a/src/EntityFramework.Sqlite/Metadata/Internal/SqliteInternalMetadataBuilderExtensions.cs b/src/EntityFramework.Sqlite/Metadata/Internal/SqliteInternalMetadataBuilderExtensions.cs index 7fb98f0981f..856e9c52be6 100644 --- a/src/EntityFramework.Sqlite/Metadata/Internal/SqliteInternalMetadataBuilderExtensions.cs +++ b/src/EntityFramework.Sqlite/Metadata/Internal/SqliteInternalMetadataBuilderExtensions.cs @@ -2,41 +2,40 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using JetBrains.Annotations; -using Microsoft.Data.Entity.Metadata; using Microsoft.Data.Entity.Metadata.Internal; namespace Microsoft.Data.Entity.Sqlite.Metadata.Internal { public static class SqliteInternalMetadataBuilderExtensions { - public static RelationalModelAnnotations Sqlite( + public static RelationalModelBuilderAnnotations Sqlite( [NotNull] this InternalModelBuilder builder, ConfigurationSource configurationSource) - => new RelationalModelAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); + => new RelationalModelBuilderAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); - public static RelationalPropertyAnnotations Sqlite( + public static RelationalPropertyBuilderAnnotations Sqlite( [NotNull] this InternalPropertyBuilder builder, ConfigurationSource configurationSource) - => new RelationalPropertyAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); + => new RelationalPropertyBuilderAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); - public static RelationalEntityTypeAnnotations Sqlite( + public static RelationalEntityTypeBuilderAnnotations Sqlite( [NotNull] this InternalEntityTypeBuilder builder, ConfigurationSource configurationSource) - => new RelationalEntityTypeAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); + => new RelationalEntityTypeBuilderAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); - public static RelationalKeyAnnotations Sqlite( + public static RelationalKeyBuilderAnnotations Sqlite( [NotNull] this InternalKeyBuilder builder, ConfigurationSource configurationSource) - => new RelationalKeyAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); + => new RelationalKeyBuilderAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); - public static RelationalIndexAnnotations Sqlite( + public static RelationalIndexBuilderAnnotations Sqlite( [NotNull] this InternalIndexBuilder builder, ConfigurationSource configurationSource) - => new RelationalIndexAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); + => new RelationalIndexBuilderAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); - public static RelationalForeignKeyAnnotations Sqlite( + public static RelationalForeignKeyBuilderAnnotations Sqlite( [NotNull] this InternalRelationshipBuilder builder, ConfigurationSource configurationSource) - => new RelationalForeignKeyAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); + => new RelationalForeignKeyBuilderAnnotations(builder, configurationSource, SqliteAnnotationNames.Prefix); } } diff --git a/test/EntityFramework.Core.FunctionalTests/InheritanceFixtureBase.cs b/test/EntityFramework.Core.FunctionalTests/InheritanceFixtureBase.cs index 67361b51155..5a979dad86f 100644 --- a/test/EntityFramework.Core.FunctionalTests/InheritanceFixtureBase.cs +++ b/test/EntityFramework.Core.FunctionalTests/InheritanceFixtureBase.cs @@ -11,70 +11,15 @@ public abstract class InheritanceFixtureBase { public virtual void OnModelCreating(ModelBuilder modelBuilder) { - // TODO: Do this with Code First when we can - - var model = modelBuilder.Model; - - var country = model.AddEntityType(typeof(Country)); - var countryIdProperty = country.AddProperty("Id", typeof(int)); - countryIdProperty.IsShadowProperty = false; - countryIdProperty.RequiresValueGenerator = true; - var countryKey = country.SetPrimaryKey(countryIdProperty); - var property1 = country.AddProperty("Name", typeof(string)); - property1.IsShadowProperty = false; - - var animal = model.AddEntityType(typeof(Animal)); - var animalSpeciesProperty = animal.AddProperty("Species", typeof(string)); - animalSpeciesProperty.IsShadowProperty = false; - animalSpeciesProperty.RequiresValueGenerator = true; - var animalKey = animal.SetPrimaryKey(animalSpeciesProperty); - var property3 = animal.AddProperty("Name", typeof(string)); - property3.IsShadowProperty = false; - var property4 = animal.AddProperty("CountryId", typeof(int)); - property4.IsShadowProperty = false; - var countryFk = animal.AddForeignKey(property4, countryKey, country); - - var bird = model.AddEntityType(typeof(Bird)); - bird.BaseType = animal; - var property5 = bird.AddProperty("IsFlightless", typeof(bool)); - property5.IsShadowProperty = false; - - var kiwi = model.AddEntityType(typeof(Kiwi)); - kiwi.BaseType = bird; - var property6 = kiwi.AddProperty("FoundOn", typeof(Island)); - property6.IsShadowProperty = false; - - var eagle = model.AddEntityType(typeof(Eagle)); - eagle.BaseType = bird; - var property7 = eagle.AddProperty("Group", typeof(EagleGroup)); - property7.IsShadowProperty = false; - - var plant = model.AddEntityType(typeof(Plant)); - var property8 = plant.AddProperty("Species", typeof(string)); - property8.IsShadowProperty = false; - var plantSpeciesProperty = property8; - plantSpeciesProperty.RequiresValueGenerator = true; - plant.SetPrimaryKey(plantSpeciesProperty); - var property9 = plant.AddProperty("Name", typeof(string)); - property9.IsShadowProperty = false; - - var flower = model.AddEntityType(typeof(Flower)); - flower.BaseType = plant; - - var rose = model.AddEntityType(typeof(Rose)); - rose.BaseType = flower; - var property10 = rose.AddProperty("HasThorns", typeof(bool)); - property10.IsShadowProperty = false; - - var daisy = model.AddEntityType(typeof(Daisy)); - daisy.BaseType = flower; - - var property11 = bird.AddProperty("EagleId", typeof(string)); - property11.IsShadowProperty = false; - var eagleFk = bird.AddForeignKey(property11, animalKey, eagle); - - country.AddNavigation("Animals", countryFk, false); - eagle.AddNavigation("Prey", eagleFk, false); + modelBuilder.Entity().BaseType(); + modelBuilder.Entity().BaseType(); + modelBuilder.Entity().BaseType(); + modelBuilder.Entity().Key(e => e.Species); + modelBuilder.Entity().BaseType(); + modelBuilder.Entity().BaseType(); + modelBuilder.Entity().BaseType(); + modelBuilder.Entity().Key(e => e.Species); + modelBuilder.Entity(); } public abstract InheritanceContext CreateContext(); diff --git a/test/EntityFramework.Core.Tests/Metadata/ForeignKeyTest.cs b/test/EntityFramework.Core.Tests/Metadata/ForeignKeyTest.cs index 4cfd2df06db..80dd9f7b53d 100644 --- a/test/EntityFramework.Core.Tests/Metadata/ForeignKeyTest.cs +++ b/test/EntityFramework.Core.Tests/Metadata/ForeignKeyTest.cs @@ -817,17 +817,21 @@ public void Finding_targets_throws_for_self_ref_foreign_keys() Assert.Throws(() => fk.ResolveOtherEntityType(fk.PrincipalEntityType)).Message); Assert.Equal( - Strings.IntraHierarchicalAmbiguousNavigation(fk.PrincipalEntityType.Name, Property.Format(fk.Properties)), + Strings.IntraHierarchicalAmbiguousNavigation(fk.PrincipalEntityType.Name, Property.Format(fk.Properties), + fk.PrincipalEntityType, fk.DeclaringEntityType), Assert.Throws(() => fk.FindNavigationFrom(fk.PrincipalEntityType)).Message); Assert.Equal( - Strings.IntraHierarchicalAmbiguousNavigation(fk.DeclaringEntityType.Name, Property.Format(fk.Properties)), + Strings.IntraHierarchicalAmbiguousNavigation(fk.DeclaringEntityType.Name, Property.Format(fk.Properties), + fk.PrincipalEntityType, fk.DeclaringEntityType), Assert.Throws(() => fk.FindNavigationFrom(fk.DeclaringEntityType)).Message); Assert.Equal( - Strings.IntraHierarchicalAmbiguousNavigation(fk.PrincipalEntityType.Name, Property.Format(fk.Properties)), + Strings.IntraHierarchicalAmbiguousNavigation(fk.PrincipalEntityType.Name, Property.Format(fk.Properties), + fk.PrincipalEntityType, fk.DeclaringEntityType), Assert.Throws(() => fk.FindNavigationTo(fk.PrincipalEntityType)).Message); Assert.Equal( - Strings.IntraHierarchicalAmbiguousNavigation(fk.DeclaringEntityType.Name, Property.Format(fk.Properties)), + Strings.IntraHierarchicalAmbiguousNavigation(fk.DeclaringEntityType.Name, Property.Format(fk.Properties), + fk.PrincipalEntityType, fk.DeclaringEntityType), Assert.Throws(() => fk.FindNavigationTo(fk.DeclaringEntityType)).Message); } @@ -851,17 +855,21 @@ public void Finding_targets_throws_for_same_hierarchy_foreign_keys() Assert.Throws(() => fk.ResolveOtherEntityType(fk.PrincipalEntityType)).Message); Assert.Equal( - Strings.IntraHierarchicalAmbiguousNavigation(fk.PrincipalEntityType.Name, Property.Format(fk.Properties)), + Strings.IntraHierarchicalAmbiguousNavigation(fk.PrincipalEntityType.Name, Property.Format(fk.Properties), + fk.PrincipalEntityType, fk.DeclaringEntityType), Assert.Throws(() => fk.FindNavigationFrom(fk.PrincipalEntityType)).Message); Assert.Equal( - Strings.IntraHierarchicalAmbiguousNavigation(fk.DeclaringEntityType.Name, Property.Format(fk.Properties)), + Strings.IntraHierarchicalAmbiguousNavigation(fk.DeclaringEntityType.Name, Property.Format(fk.Properties), + fk.PrincipalEntityType, fk.DeclaringEntityType), Assert.Throws(() => fk.FindNavigationFrom(fk.DeclaringEntityType)).Message); Assert.Equal( - Strings.IntraHierarchicalAmbiguousNavigation(fk.PrincipalEntityType.Name, Property.Format(fk.Properties)), + Strings.IntraHierarchicalAmbiguousNavigation(fk.PrincipalEntityType.Name, Property.Format(fk.Properties), + fk.PrincipalEntityType, fk.DeclaringEntityType), Assert.Throws(() => fk.FindNavigationTo(fk.PrincipalEntityType)).Message); Assert.Equal( - Strings.IntraHierarchicalAmbiguousNavigation(fk.DeclaringEntityType.Name, Property.Format(fk.Properties)), + Strings.IntraHierarchicalAmbiguousNavigation(fk.DeclaringEntityType.Name, Property.Format(fk.Properties), + fk.PrincipalEntityType, fk.DeclaringEntityType), Assert.Throws(() => fk.FindNavigationTo(fk.DeclaringEntityType)).Message); } diff --git a/test/EntityFramework.Core.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs b/test/EntityFramework.Core.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs index 55b7ee996b2..774a1c61c71 100644 --- a/test/EntityFramework.Core.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs +++ b/test/EntityFramework.Core.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs @@ -3,7 +3,6 @@ using System.Reflection; using Microsoft.Data.Entity.Metadata.Conventions; -using Microsoft.Data.Entity.Metadata.Conventions.Internal; using Xunit; namespace Microsoft.Data.Entity.Metadata.Internal @@ -204,6 +203,69 @@ public void Can_only_override_existing_Required_value_explicitly() Assert.True(metadata.IsNullable.Value); } + + [Fact] + public void Can_only_override_lower_or_equal_source_ReadOnlyAfterSave() + { + var builder = CreateInternalPropertyBuilder(); + var metadata = builder.Metadata; + + Assert.True(builder.ReadOnlyAfterSave(true, ConfigurationSource.DataAnnotation)); + Assert.True(builder.ReadOnlyAfterSave(false, ConfigurationSource.DataAnnotation)); + + Assert.False(metadata.IsReadOnlyAfterSave.Value); + + Assert.False(builder.ReadOnlyAfterSave(true, ConfigurationSource.Convention)); + Assert.False(metadata.IsReadOnlyAfterSave.Value); + } + + [Fact] + public void Can_only_override_existing_ReadOnlyAfterSave_value_explicitly() + { + var builder = CreateInternalPropertyBuilder(); + var metadata = builder.Metadata; + metadata.IsReadOnlyAfterSave = false; + + Assert.True(builder.ReadOnlyAfterSave(false, ConfigurationSource.DataAnnotation)); + Assert.False(builder.ReadOnlyAfterSave(true, ConfigurationSource.DataAnnotation)); + + Assert.False(metadata.IsReadOnlyAfterSave.Value); + + Assert.True(builder.ReadOnlyAfterSave(true, ConfigurationSource.Explicit)); + Assert.True(metadata.IsReadOnlyAfterSave.Value); + } + + [Fact] + public void Can_only_override_lower_or_equal_source_ReadOnlyBeforeSave() + { + var builder = CreateInternalPropertyBuilder(); + var metadata = builder.Metadata; + + Assert.True(builder.ReadOnlyBeforeSave(true, ConfigurationSource.DataAnnotation)); + Assert.True(builder.ReadOnlyBeforeSave(false, ConfigurationSource.DataAnnotation)); + + Assert.False(metadata.IsReadOnlyBeforeSave.Value); + + Assert.False(builder.ReadOnlyBeforeSave(true, ConfigurationSource.Convention)); + Assert.False(metadata.IsReadOnlyBeforeSave.Value); + } + + [Fact] + public void Can_only_override_existing_ReadOnlyBeforeSave_value_explicitly() + { + var builder = CreateInternalPropertyBuilder(); + var metadata = builder.Metadata; + metadata.IsReadOnlyBeforeSave = true; + + Assert.True(builder.ReadOnlyBeforeSave(true, ConfigurationSource.DataAnnotation)); + Assert.False(builder.ReadOnlyBeforeSave(false, ConfigurationSource.DataAnnotation)); + + Assert.True(metadata.IsReadOnlyBeforeSave.Value); + + Assert.True(builder.ReadOnlyBeforeSave(false, ConfigurationSource.Explicit)); + Assert.False(metadata.IsReadOnlyBeforeSave.Value); + } + [Fact] public void Can_only_override_lower_or_equal_source_Shadow() { diff --git a/test/EntityFramework.Core.Tests/ModelBuilderTest/InheritanceTestBase.cs b/test/EntityFramework.Core.Tests/ModelBuilderTest/InheritanceTestBase.cs index 3f866555001..96fa154cb13 100644 --- a/test/EntityFramework.Core.Tests/ModelBuilderTest/InheritanceTestBase.cs +++ b/test/EntityFramework.Core.Tests/ModelBuilderTest/InheritanceTestBase.cs @@ -29,7 +29,7 @@ public virtual void Can_set_switch_and_remove_base_type() var initialProperties = pickleClone.GetProperties(); var initialIndexes = pickleClone.GetIndexes(); var initialForeignKey = pickleClone.GetForeignKeys(); - var initialReferencingForeignKey = pickleClone.GetReferencingForeignKeys(); + var initialReferencingForeignKey = pickleClone.FindReferencingForeignKeys(); var initialKeys = pickleClone.GetKeys(); pickleBuilder.BaseEntity(); diff --git a/test/EntityFramework.Relational.FunctionalTests/InheritanceRelationalFixture.cs b/test/EntityFramework.Relational.FunctionalTests/InheritanceRelationalFixture.cs index c8090cfc2dd..832854911fa 100644 --- a/test/EntityFramework.Relational.FunctionalTests/InheritanceRelationalFixture.cs +++ b/test/EntityFramework.Relational.FunctionalTests/InheritanceRelationalFixture.cs @@ -1,9 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using Microsoft.Data.Entity.FunctionalTests.TestModels.Inheritance; -using Microsoft.Data.Entity.Metadata; namespace Microsoft.Data.Entity.FunctionalTests { @@ -13,35 +11,14 @@ public override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - // TODO: Code First this + // TODO: remove this when the discriminator convention is added + modelBuilder.Entity().Discriminator() + .HasValue(typeof(Eagle), "Eagle") + .HasValue(typeof(Kiwi), "Kiwi"); - var animal = modelBuilder.Entity().Metadata; - - var animalDiscriminatorProperty = animal.AddProperty("Discriminator", typeof(string)); - animalDiscriminatorProperty.IsNullable = false; - //animalDiscriminatorProperty.IsReadOnlyBeforeSave = true; // #2132 - animalDiscriminatorProperty.IsReadOnlyAfterSave = true; - animalDiscriminatorProperty.RequiresValueGenerator = true; - - animal.Relational().DiscriminatorProperty = animalDiscriminatorProperty; - - var plant = modelBuilder.Entity().Metadata; - - var plantDiscriminatorProperty = plant.AddProperty("Genus", typeof(PlantGenus)); - plantDiscriminatorProperty.IsShadowProperty = false; - - plantDiscriminatorProperty.IsNullable = false; - //plantDiscriminatorProperty.IsReadOnlyBeforeSave = true; // #2132 - plantDiscriminatorProperty.IsReadOnlyAfterSave = true; - plantDiscriminatorProperty.RequiresValueGenerator = true; - - plant.Relational().DiscriminatorProperty = plantDiscriminatorProperty; - - var rose = modelBuilder.Entity().Metadata; - rose.Relational().DiscriminatorValue = PlantGenus.Rose; - - var daisy = modelBuilder.Entity().Metadata; - daisy.Relational().DiscriminatorValue = PlantGenus.Daisy; + modelBuilder.Entity().Discriminator(p => p.Genus) + .HasValue(typeof(Rose), PlantGenus.Rose) + .HasValue(typeof(Daisy), PlantGenus.Daisy); } } } diff --git a/test/EntityFramework.Relational.Tests/Metadata/InternalRelationalMetadataBuilderExtensionsTest.cs b/test/EntityFramework.Relational.Tests/Metadata/InternalRelationalMetadataBuilderExtensionsTest.cs index 5586edff449..fc3bff30c58 100644 --- a/test/EntityFramework.Relational.Tests/Metadata/InternalRelationalMetadataBuilderExtensionsTest.cs +++ b/test/EntityFramework.Relational.Tests/Metadata/InternalRelationalMetadataBuilderExtensionsTest.cs @@ -1,8 +1,12 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; +using System.Reflection; +using Microsoft.Data.Entity.Metadata.Builders; using Microsoft.Data.Entity.Metadata.Conventions; using Microsoft.Data.Entity.Metadata.Internal; +using Microsoft.Data.Entity.Relational.Internal; using Xunit; namespace Microsoft.Data.Entity.Metadata @@ -27,13 +31,13 @@ public void Can_access_entity_type() { var typeBuilder = CreateBuilder().Entity(typeof(Splot), ConfigurationSource.Convention); - typeBuilder.Relational(ConfigurationSource.Convention).TableName = "Splew"; + Assert.True(typeBuilder.Relational(ConfigurationSource.Convention).ToTable("Splew")); Assert.Equal("Splew", typeBuilder.Metadata.Relational().TableName); - typeBuilder.Relational(ConfigurationSource.DataAnnotation).TableName = "Splow"; + Assert.True(typeBuilder.Relational(ConfigurationSource.DataAnnotation).ToTable("Splow")); Assert.Equal("Splow", typeBuilder.Metadata.Relational().TableName); - typeBuilder.Relational(ConfigurationSource.Convention).TableName = "Splod"; + Assert.False(typeBuilder.Relational(ConfigurationSource.Convention).ToTable("Splod")); Assert.Equal("Splow", typeBuilder.Metadata.Relational().TableName); } @@ -44,13 +48,13 @@ public void Can_access_property() .Entity(typeof(Splot), ConfigurationSource.Convention) .Property("Id", typeof(int), ConfigurationSource.Convention); - propertyBuilder.Relational(ConfigurationSource.Convention).ColumnName = "Splew"; + Assert.True(propertyBuilder.Relational(ConfigurationSource.Convention).ColumnName("Splew")); Assert.Equal("Splew", propertyBuilder.Metadata.Relational().ColumnName); - propertyBuilder.Relational(ConfigurationSource.DataAnnotation).ColumnName = "Splow"; + Assert.True(propertyBuilder.Relational(ConfigurationSource.DataAnnotation).ColumnName("Splow")); Assert.Equal("Splow", propertyBuilder.Metadata.Relational().ColumnName); - propertyBuilder.Relational(ConfigurationSource.Convention).ColumnName = "Splod"; + Assert.False(propertyBuilder.Relational(ConfigurationSource.Convention).ColumnName("Splod")); Assert.Equal("Splow", propertyBuilder.Metadata.Relational().ColumnName); } @@ -62,13 +66,13 @@ public void Can_access_key() var property = entityTypeBuilder.Property("Id", typeof(int), ConfigurationSource.Convention).Metadata; var keyBuilder = entityTypeBuilder.Key(new[] { property }, ConfigurationSource.Convention); - keyBuilder.Relational(ConfigurationSource.Convention).Name = "Splew"; + Assert.True(keyBuilder.Relational(ConfigurationSource.Convention).Name("Splew")); Assert.Equal("Splew", keyBuilder.Metadata.Relational().Name); - keyBuilder.Relational(ConfigurationSource.DataAnnotation).Name = "Splow"; + Assert.True(keyBuilder.Relational(ConfigurationSource.DataAnnotation).Name("Splow")); Assert.Equal("Splow", keyBuilder.Metadata.Relational().Name); - keyBuilder.Relational(ConfigurationSource.Convention).Name = "Splod"; + Assert.False(keyBuilder.Relational(ConfigurationSource.Convention).Name("Splod")); Assert.Equal("Splow", keyBuilder.Metadata.Relational().Name); } @@ -80,13 +84,13 @@ public void Can_access_index() entityTypeBuilder.Property("Id", typeof(int), ConfigurationSource.Convention); var indexBuilder = entityTypeBuilder.Index(new[] { "Id" }, ConfigurationSource.Convention); - indexBuilder.Relational(ConfigurationSource.Convention).Name = "Splew"; + Assert.True(indexBuilder.Relational(ConfigurationSource.Convention).Name("Splew")); Assert.Equal("Splew", indexBuilder.Metadata.Relational().Name); - indexBuilder.Relational(ConfigurationSource.DataAnnotation).Name = "Splow"; + Assert.True(indexBuilder.Relational(ConfigurationSource.DataAnnotation).Name("Splow")); Assert.Equal("Splow", indexBuilder.Metadata.Relational().Name); - indexBuilder.Relational(ConfigurationSource.Convention).Name = "Splod"; + Assert.False(indexBuilder.Relational(ConfigurationSource.Convention).Name("Splod")); Assert.Equal("Splow", indexBuilder.Metadata.Relational().Name); } @@ -98,17 +102,140 @@ public void Can_access_relationship() entityTypeBuilder.Property("Id", typeof(int), ConfigurationSource.Convention); var relationshipBuilder = entityTypeBuilder.ForeignKey("Splot", new[] { "Id" }, ConfigurationSource.Convention); - relationshipBuilder.Relational(ConfigurationSource.Convention).Name = "Splew"; + Assert.True(relationshipBuilder.Relational(ConfigurationSource.Convention).Name("Splew")); Assert.Equal("Splew", relationshipBuilder.Metadata.Relational().Name); - relationshipBuilder.Relational(ConfigurationSource.DataAnnotation).Name = "Splow"; + Assert.True(relationshipBuilder.Relational(ConfigurationSource.DataAnnotation).Name("Splow")); Assert.Equal("Splow", relationshipBuilder.Metadata.Relational().Name); - relationshipBuilder.Relational(ConfigurationSource.Convention).Name = "Splod"; + Assert.False(relationshipBuilder.Relational(ConfigurationSource.Convention).Name("Splod")); Assert.Equal("Splow", relationshipBuilder.Metadata.Relational().Name); } + [Fact] + public void Can_access_discriminator() + { + var typeBuilder = CreateBuilder().Entity(typeof(Splot), ConfigurationSource.Convention); + + Assert.NotNull(typeBuilder.Relational(ConfigurationSource.Convention).Discriminator()); + Assert.Equal("Discriminator", typeBuilder.Metadata.Relational().DiscriminatorProperty.Name); + Assert.Equal(typeof(string), typeBuilder.Metadata.Relational().DiscriminatorProperty.ClrType); + + Assert.NotNull(typeBuilder.Relational(ConfigurationSource.Convention).Discriminator("Splod", typeof(int?))); + Assert.Equal("Splod", typeBuilder.Metadata.Relational().DiscriminatorProperty.Name); + Assert.Equal(typeof(int?), typeBuilder.Metadata.Relational().DiscriminatorProperty.ClrType); + + Assert.NotNull(typeBuilder.Relational(ConfigurationSource.DataAnnotation).Discriminator(Splot.SplowedProperty)); + Assert.Equal(Splot.SplowedProperty.Name, typeBuilder.Metadata.Relational().DiscriminatorProperty.Name); + Assert.Equal(typeof(int?), typeBuilder.Metadata.Relational().DiscriminatorProperty.ClrType); + + Assert.Null(typeBuilder.Relational(ConfigurationSource.Convention).Discriminator("Splew", typeof(int?))); + Assert.Equal(Splot.SplowedProperty.Name, typeBuilder.Metadata.Relational().DiscriminatorProperty.Name); + Assert.Equal(typeof(int?), typeBuilder.Metadata.Relational().DiscriminatorProperty.ClrType); + + } + + [Fact] + public void Can_access_discriminator_value() + { + var typeBuilder = CreateBuilder().Entity("Splot", ConfigurationSource.Convention); + + var discriminatorBuilder = typeBuilder.Relational(ConfigurationSource.Convention).Discriminator(Splot.SplowedProperty); + Assert.NotNull(discriminatorBuilder.HasValue("Splot", 1)); + Assert.NotNull(discriminatorBuilder.HasValue("Splow", 2)); + Assert.NotNull(discriminatorBuilder.HasValue("Splod", 3)); + Assert.Equal(1, typeBuilder.Metadata.Relational().DiscriminatorValue); + Assert.Equal(2, typeBuilder.ModelBuilder.Entity("Splow", ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + Assert.Equal(3, typeBuilder.ModelBuilder.Entity("Splod", ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + + discriminatorBuilder = typeBuilder.Relational(ConfigurationSource.DataAnnotation).Discriminator(); + Assert.NotNull(discriminatorBuilder.HasValue("Splot", 4)); + Assert.NotNull(discriminatorBuilder.HasValue("Splow", 5)); + Assert.NotNull(discriminatorBuilder.HasValue("Splod", 6)); + Assert.Equal(4, typeBuilder.Metadata.Relational().DiscriminatorValue); + Assert.Equal(5, typeBuilder.ModelBuilder.Entity("Splow", ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + Assert.Equal(6, typeBuilder.ModelBuilder.Entity("Splod", ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + + discriminatorBuilder = typeBuilder.Relational(ConfigurationSource.Convention).Discriminator(); + Assert.Null(discriminatorBuilder.HasValue("Splot", 1)); + Assert.Null(discriminatorBuilder.HasValue("Splow", 2)); + Assert.Null(discriminatorBuilder.HasValue("Splod", 3)); + Assert.Equal(4, typeBuilder.Metadata.Relational().DiscriminatorValue); + Assert.Equal(5, typeBuilder.ModelBuilder.Entity("Splow", ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + Assert.Equal(6, typeBuilder.ModelBuilder.Entity("Splod", ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + } + + [Fact] + public void Can_access_discriminator_value_generic() + { + var typeBuilder = CreateBuilder().Entity(typeof(Splot), ConfigurationSource.Convention); + + var discriminatorBuilder = new DiscriminatorBuilder( + typeBuilder.Relational(ConfigurationSource.Convention).Discriminator(Splot.SplowedProperty)); + Assert.NotNull(discriminatorBuilder.HasValue(typeof(Splot), 1)); + Assert.NotNull(discriminatorBuilder.HasValue(typeof(Splow), 2)); + Assert.NotNull(discriminatorBuilder.HasValue(typeof(Splod), 3)); + Assert.Equal(1, typeBuilder.Metadata.Relational().DiscriminatorValue); + Assert.Equal(2, typeBuilder.ModelBuilder.Entity(typeof(Splow), ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + Assert.Equal(3, typeBuilder.ModelBuilder.Entity(typeof(Splod), ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + + discriminatorBuilder = new DiscriminatorBuilder( + typeBuilder.Relational(ConfigurationSource.DataAnnotation).Discriminator()); + Assert.NotNull(discriminatorBuilder.HasValue(typeof(Splot), 4)); + Assert.NotNull(discriminatorBuilder.HasValue(typeof(Splow), 5)); + Assert.NotNull(discriminatorBuilder.HasValue(typeof(Splod), 6)); + Assert.Equal(4, typeBuilder.Metadata.Relational().DiscriminatorValue); + Assert.Equal(5, typeBuilder.ModelBuilder.Entity(typeof(Splow), ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + Assert.Equal(6, typeBuilder.ModelBuilder.Entity(typeof(Splod), ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + + discriminatorBuilder = new DiscriminatorBuilder( + typeBuilder.Relational(ConfigurationSource.Convention).Discriminator()); + Assert.Null(discriminatorBuilder.HasValue(typeof(Splot), 1)); + Assert.Null(discriminatorBuilder.HasValue(typeof(Splow), 2)); + Assert.Null(discriminatorBuilder.HasValue(typeof(Splod), 3)); + Assert.Equal(4, typeBuilder.Metadata.Relational().DiscriminatorValue); + Assert.Equal(5, typeBuilder.ModelBuilder.Entity(typeof(Splow), ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + Assert.Equal(6, typeBuilder.ModelBuilder.Entity(typeof(Splod), ConfigurationSource.Convention) + .Metadata.Relational().DiscriminatorValue); + } + + [Fact] + public void DiscriminatorValue_throws_if_base_cannot_be_set() + { + var modelBuilder = CreateBuilder(); + var typeBuilder = modelBuilder.Entity("Splot", ConfigurationSource.Convention); + var nonDerivedTypeBuilder = modelBuilder.Entity("Splow", ConfigurationSource.Convention); + nonDerivedTypeBuilder.BaseType( + modelBuilder.Entity("Splod", ConfigurationSource.Convention).Metadata, ConfigurationSource.Explicit); + + var discriminatorBuilder = typeBuilder.Relational(ConfigurationSource.Convention).Discriminator(); + Assert.Equal(Strings.DiscriminatorEntityTypeNotDerived("Splow", "Splot"), + Assert.Throws(() => discriminatorBuilder.HasValue("Splow", "1")).Message); + } + private class Splot + { + public static readonly PropertyInfo SplowedProperty = typeof(Splot).GetProperty("Splowed"); + + public int? Splowed { get; set; } + } + + private class Splow : Splot + { + } + + private class Splod : Splow { } } diff --git a/test/EntityFramework.Relational.Tests/Metadata/RelationalBuilderExtensionsTest.cs b/test/EntityFramework.Relational.Tests/Metadata/RelationalBuilderExtensionsTest.cs index 30b1f0fd5e6..8cd8e340b9d 100644 --- a/test/EntityFramework.Relational.Tests/Metadata/RelationalBuilderExtensionsTest.cs +++ b/test/EntityFramework.Relational.Tests/Metadata/RelationalBuilderExtensionsTest.cs @@ -13,7 +13,7 @@ namespace Microsoft.Data.Entity.Metadata.Tests public class RelationalBuilderExtensionsTest { [Fact] - public void Can_set_column_name_with_convention_builder() + public void Can_set_column_name() { var modelBuilder = CreateConventionModelBuilder(); @@ -29,7 +29,7 @@ public void Can_set_column_name_with_convention_builder() } [Fact] - public void Can_set_column_type_with_convention_builder() + public void Can_set_column_type() { var modelBuilder = CreateConventionModelBuilder(); @@ -44,7 +44,7 @@ public void Can_set_column_type_with_convention_builder() } [Fact] - public void Can_set_column_default_expression_with_convention_builder() + public void Can_set_column_default_expression() { var modelBuilder = CreateConventionModelBuilder(); @@ -60,7 +60,7 @@ public void Can_set_column_default_expression_with_convention_builder() } [Fact] - public void Can_set_column_computed_expression_with_convention_builder() + public void Can_set_column_computed_expression() { var modelBuilder = CreateConventionModelBuilder(); @@ -76,7 +76,7 @@ public void Can_set_column_computed_expression_with_convention_builder() } [Fact] - public void Can_set_column_default_value_with_convention_builder() + public void Can_set_column_default_value() { var modelBuilder = CreateConventionModelBuilder(); var guid = new Guid("{3FDFC4F5-AEAB-4D72-9C96-201E004349FA}"); @@ -92,7 +92,7 @@ public void Can_set_column_default_value_with_convention_builder() } [Fact] - public void Can_set_key_name_with_convention_builder() + public void Can_set_key_name() { var modelBuilder = CreateConventionModelBuilder(); @@ -107,7 +107,7 @@ public void Can_set_key_name_with_convention_builder() } [Fact] - public void Can_set_foreign_key_name_for_one_to_many_with_convention_builder() + public void Can_set_foreign_key_name_for_one_to_many() { var modelBuilder = CreateConventionModelBuilder(); @@ -127,7 +127,7 @@ public void Can_set_foreign_key_name_for_one_to_many_with_convention_builder() } [Fact] - public void Can_set_foreign_key_name_for_one_to_many_with_FK_specified_with_convention_builder() + public void Can_set_foreign_key_name_for_one_to_many_with_FK_specified() { var modelBuilder = CreateConventionModelBuilder(); @@ -142,7 +142,7 @@ public void Can_set_foreign_key_name_for_one_to_many_with_FK_specified_with_conv } [Fact] - public void Can_set_foreign_key_name_for_many_to_one_with_convention_builder() + public void Can_set_foreign_key_name_for_many_to_one() { var modelBuilder = CreateConventionModelBuilder(); @@ -162,7 +162,7 @@ public void Can_set_foreign_key_name_for_many_to_one_with_convention_builder() } [Fact] - public void Can_set_foreign_key_name_for_many_to_one_with_FK_specified_with_convention_builder() + public void Can_set_foreign_key_name_for_many_to_one_with_FK_specified() { var modelBuilder = CreateConventionModelBuilder(); @@ -177,7 +177,7 @@ public void Can_set_foreign_key_name_for_many_to_one_with_FK_specified_with_conv } [Fact] - public void Can_set_foreign_key_name_for_one_to_one_with_convention_builder() + public void Can_set_foreign_key_name_for_one_to_one() { var modelBuilder = CreateConventionModelBuilder(); @@ -198,7 +198,7 @@ public void Can_set_foreign_key_name_for_one_to_one_with_convention_builder() } [Fact] - public void Can_set_foreign_key_name_for_one_to_one_with_FK_specified_with_convention_builder() + public void Can_set_foreign_key_name_for_one_to_one_with_FK_specified() { var modelBuilder = CreateConventionModelBuilder(); @@ -213,7 +213,7 @@ public void Can_set_foreign_key_name_for_one_to_one_with_FK_specified_with_conve } [Fact] - public void Can_set_index_name_with_convention_builder() + public void Can_set_index_name() { var modelBuilder = CreateConventionModelBuilder(); @@ -228,7 +228,7 @@ public void Can_set_index_name_with_convention_builder() } [Fact] - public void Can_set_table_name_with_convention_builder() + public void Can_set_table_name() { var modelBuilder = CreateConventionModelBuilder(); @@ -243,7 +243,7 @@ public void Can_set_table_name_with_convention_builder() } [Fact] - public void Can_set_table_name_with_convention_builder_non_generic() + public void Can_set_table_name_non_generic() { var modelBuilder = CreateConventionModelBuilder(); @@ -258,7 +258,7 @@ public void Can_set_table_name_with_convention_builder_non_generic() } [Fact] - public void Can_set_table_and_schema_name_with_convention_builder() + public void Can_set_table_and_schema_name() { var modelBuilder = CreateConventionModelBuilder(); @@ -274,7 +274,7 @@ public void Can_set_table_and_schema_name_with_convention_builder() } [Fact] - public void Can_set_table_and_schema_name_with_convention_builder_non_generic() + public void Can_set_table_and_schema_name_non_generic() { var modelBuilder = CreateConventionModelBuilder(); @@ -288,9 +288,99 @@ public void Can_set_table_and_schema_name_with_convention_builder_non_generic() Assert.Equal("Customizer", entityType.Relational().TableName); Assert.Equal("db0", entityType.Relational().Schema); } + + [Fact] + public void Can_set_discriminator_value_using_property_expression() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder + .Entity() + .Discriminator(b => b.Name) + .HasValue(typeof(Customer), "1") + .HasValue(typeof(SpecialCustomer), "2"); + + var entityType = modelBuilder.Model.GetEntityType(typeof(Customer)); + Assert.Equal("Name", entityType.Relational().DiscriminatorProperty.Name); + Assert.Equal(typeof(string), entityType.Relational().DiscriminatorProperty.ClrType); + Assert.Equal("1", entityType.Relational().DiscriminatorValue); + Assert.Equal("2", modelBuilder.Model.GetEntityType(typeof(SpecialCustomer)).Relational().DiscriminatorValue); + } + + [Fact] + public void Can_set_discriminator_value_using_property_name() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder + .Entity() + .Discriminator("Name", typeof(string)) + .HasValue(typeof(Customer), "1") + .HasValue(typeof(SpecialCustomer), "2"); + + var entityType = modelBuilder.Model.GetEntityType(typeof(Customer)); + Assert.Equal("Name", entityType.Relational().DiscriminatorProperty.Name); + Assert.Equal(typeof(string), entityType.Relational().DiscriminatorProperty.ClrType); + Assert.Equal("1", entityType.Relational().DiscriminatorValue); + Assert.Equal("2", modelBuilder.Model.GetEntityType(typeof(SpecialCustomer)).Relational().DiscriminatorValue); + } + + [Fact] + public void Can_set_discriminator_value_non_generic() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder + .Entity(typeof(Customer)) + .Discriminator("Name", typeof(string)) + .HasValue(typeof(Customer), "1") + .HasValue(typeof(SpecialCustomer), "2"); + + var entityType = modelBuilder.Model.GetEntityType(typeof(Customer)); + Assert.Equal("Name", entityType.Relational().DiscriminatorProperty.Name); + Assert.Equal(typeof(string), entityType.Relational().DiscriminatorProperty.ClrType); + Assert.Equal("1", entityType.Relational().DiscriminatorValue); + Assert.Equal("2", modelBuilder.Model.GetEntityType(typeof(SpecialCustomer)).Relational().DiscriminatorValue); + } + + [Fact] + public void Can_set_discriminator_value_shadow_entity() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder + .Entity(typeof(Customer).FullName) + .Discriminator("Name", typeof(string)) + .HasValue(typeof(Customer).FullName, "1") + .HasValue(typeof(SpecialCustomer).FullName, "2"); + + var entityType = modelBuilder.Model.GetEntityType(typeof(Customer)); + Assert.Equal("Name", entityType.Relational().DiscriminatorProperty.Name); + Assert.Equal(typeof(string), entityType.Relational().DiscriminatorProperty.ClrType); + Assert.Equal("1", entityType.Relational().DiscriminatorValue); + Assert.Equal("2", modelBuilder.Model.GetEntityType(typeof(SpecialCustomer)).Relational().DiscriminatorValue); + } + + [Fact] + public void Can_set_default_discriminator_value() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder + .Entity(typeof(Customer)) + .Discriminator() + .HasValue(typeof(Customer), "1") + .HasValue(typeof(SpecialCustomer), "2"); + + var entityType = modelBuilder.Model.GetEntityType(typeof(Customer)); + Assert.Equal("Discriminator", entityType.Relational().DiscriminatorProperty.Name); + Assert.Equal(typeof(string), entityType.Relational().DiscriminatorProperty.ClrType); + Assert.Equal("1", entityType.Relational().DiscriminatorValue); + Assert.Equal("2", modelBuilder.Model.GetEntityType(typeof(SpecialCustomer)).Relational().DiscriminatorValue); + } [Fact] - public void Can_create_named_sequence_with_convention_builder() + public void Can_create_named_sequence() { var modelBuilder = CreateConventionModelBuilder(); @@ -313,7 +403,7 @@ private static void ValidateNamedSequence(ISequence sequence) } [Fact] - public void Can_create_schema_named_sequence_with_convention_builder() + public void Can_create_schema_named_sequence() { var modelBuilder = CreateConventionModelBuilder(); @@ -336,7 +426,7 @@ private static void ValidateSchemaNamedSequence(ISequence sequence) } [Fact] - public void Can_create_named_sequence_with_specific_facets_with_convention_builder() + public void Can_create_named_sequence_with_specific_facets() { var modelBuilder = CreateConventionModelBuilder(); @@ -353,7 +443,7 @@ public void Can_create_named_sequence_with_specific_facets_with_convention_build } [Fact] - public void Can_create_named_sequence_with_specific_facets_with_convention_builder_non_generic() + public void Can_create_named_sequence_with_specific_facets_non_generic() { var modelBuilder = CreateConventionModelBuilder(); @@ -419,7 +509,7 @@ private static void ValidateNamedSpecificSequence(ISequence sequence) } [Fact] - public void Can_create_schema_named_sequence_with_specific_facets_with_convention_builder() + public void Can_create_schema_named_sequence_with_specific_facets() { var modelBuilder = CreateConventionModelBuilder(); @@ -436,7 +526,7 @@ public void Can_create_schema_named_sequence_with_specific_facets_with_conventio } [Fact] - public void Can_create_schema_named_sequence_with_specific_facets_with_convention_builder_non_generic() + public void Can_create_schema_named_sequence_with_specific_facets_non_generic() { var modelBuilder = CreateConventionModelBuilder(); @@ -671,10 +761,7 @@ private void AssertIsGeneric(ReferenceReferenceBuilder _) { } - protected virtual ModelBuilder CreateConventionModelBuilder() - { - return RelationalTestHelpers.Instance.CreateConventionBuilder(); - } + protected virtual ModelBuilder CreateConventionModelBuilder() => RelationalTestHelpers.Instance.CreateConventionBuilder(); private static void ValidateSchemaNamedSpecificSequence(ISequence sequence) { @@ -694,6 +781,10 @@ private class Customer public IEnumerable Orders { get; set; } } + + private class SpecialCustomer : Customer + { + } private class Order { diff --git a/test/EntityFramework.Relational.Tests/Metadata/RelationalMetadataExtensionsTest.cs b/test/EntityFramework.Relational.Tests/Metadata/RelationalMetadataExtensionsTest.cs index 7d17f3efdca..a82b4796c01 100644 --- a/test/EntityFramework.Relational.Tests/Metadata/RelationalMetadataExtensionsTest.cs +++ b/test/EntityFramework.Relational.Tests/Metadata/RelationalMetadataExtensionsTest.cs @@ -3,6 +3,7 @@ using System; using Microsoft.Data.Entity.Metadata.Conventions; +using Microsoft.Data.Entity.Relational.Internal; using Xunit; namespace Microsoft.Data.Entity.Metadata.Tests @@ -208,6 +209,86 @@ public void Can_get_and_set_index_name() Assert.Equal("IX_Customer_Id", index.Relational().Name); } + [Fact] + public void Can_get_and_set_discriminator() + { + var modelBuilder = new ModelBuilder(new ConventionSet()); + + var entityType = modelBuilder + .Entity() + .Metadata; + + Assert.Null(entityType.Relational().DiscriminatorProperty); + + var property = entityType.AddProperty("D"); + + entityType.Relational().DiscriminatorProperty = property; + + Assert.Same(property, entityType.Relational().DiscriminatorProperty); + + entityType.Relational().DiscriminatorProperty = null; + + Assert.Null(entityType.Relational().DiscriminatorProperty); + } + + [Fact] + public void Can_get_and_set_discriminator_value() + { + var modelBuilder = new ModelBuilder(new ConventionSet()); + + var entityType = modelBuilder + .Entity() + .Metadata; + + var property = entityType.AddProperty("D"); + property.ClrType = typeof(string); + entityType.Relational().DiscriminatorProperty = property; + + Assert.Null(entityType.Relational().DiscriminatorValue); + + entityType.Relational().DiscriminatorValue = "V"; + + Assert.Equal("V", entityType.Relational().DiscriminatorValue); + + entityType.Relational().DiscriminatorValue = null; + + Assert.Null(entityType.Relational().DiscriminatorValue); + } + + [Fact] + public void Setting_discriminator_value_when_discriminator_not_set_throws() + { + var modelBuilder = new ModelBuilder(new ConventionSet()); + + var entityType = modelBuilder + .Entity() + .Metadata; + + Assert.Equal(Strings.NoDiscriminator("Customer", "Customer"), + Assert.Throws(() => + entityType.Relational().DiscriminatorValue = "V").Message); + } + + [Fact] + public void Setting_incompatible_discriminator_value_throws() + { + var modelBuilder = new ModelBuilder(new ConventionSet()); + + var entityType = modelBuilder + .Entity() + .Metadata; + + var property = entityType.AddProperty("D"); + property.ClrType = typeof(int); + entityType.Relational().DiscriminatorProperty = property; + + Assert.Equal(Strings.DiscriminitatorValueIncompatible("V", "D", typeof(int)), + Assert.Throws(() => + entityType.Relational().DiscriminatorValue = "V").Message); + + entityType.Relational().DiscriminatorValue = null; + } + [Fact] public void Can_get_and_set_sequence() { diff --git a/test/EntityFramework.SqlServer.FunctionalTests/InheritanceSqlServerFixture.cs b/test/EntityFramework.SqlServer.FunctionalTests/InheritanceSqlServerFixture.cs index def2d63809d..571444cf05a 100644 --- a/test/EntityFramework.SqlServer.FunctionalTests/InheritanceSqlServerFixture.cs +++ b/test/EntityFramework.SqlServer.FunctionalTests/InheritanceSqlServerFixture.cs @@ -55,6 +55,7 @@ CREATE TABLE Plant( Genus int NOT NULL, Species nvarchar(100) NOT NULL PRIMARY KEY, Name nvarchar(100) NOT NULL, + CountryId int FOREIGN KEY REFERENCES Country (Id), HasThorns bit );"); @@ -64,9 +65,6 @@ HasThorns bit } } - public override InheritanceContext CreateContext() - { - return new InheritanceContext(_serviceProvider, _options); - } + public override InheritanceContext CreateContext() => new InheritanceContext(_serviceProvider, _options); } } diff --git a/test/EntityFramework.SqlServer.FunctionalTests/InheritanceSqlServerTest.cs b/test/EntityFramework.SqlServer.FunctionalTests/InheritanceSqlServerTest.cs index 2781d8500e5..c100bdafb99 100644 --- a/test/EntityFramework.SqlServer.FunctionalTests/InheritanceSqlServerTest.cs +++ b/test/EntityFramework.SqlServer.FunctionalTests/InheritanceSqlServerTest.cs @@ -60,7 +60,7 @@ public override void Can_use_of_type_rose() base.Can_use_of_type_rose(); Assert.Equal( - @"SELECT [p].[Species], [p].[Genus], [p].[Name], [p].[HasThorns] + @"SELECT [p].[Species], [p].[CountryId], [p].[Genus], [p].[Name], [p].[HasThorns] FROM [Plant] AS [p] WHERE [p].[Genus] = 0", Sql); @@ -83,7 +83,7 @@ public override void Can_query_all_plants() base.Can_query_all_plants(); Assert.Equal( - @"SELECT [a].[Species], [a].[Genus], [a].[Name], [a].[HasThorns] + @"SELECT [a].[Species], [a].[CountryId], [a].[Genus], [a].[Name], [a].[HasThorns] FROM [Plant] AS [a] WHERE [a].[Genus] IN (0, 1) ORDER BY [a].[Species]", @@ -130,7 +130,7 @@ public override void Can_query_just_roses() base.Can_query_just_roses(); Assert.Equal( - @"SELECT TOP(2) [p].[Species], [p].[Genus], [p].[Name], [p].[HasThorns] + @"SELECT TOP(2) [p].[Species], [p].[CountryId], [p].[Genus], [p].[Name], [p].[HasThorns] FROM [Plant] AS [p] WHERE [p].[Genus] = 0", Sql diff --git a/test/EntityFramework.SqlServer.Tests/Metadata/InternalSqlServerMetadataBuilderExtensionsTest.cs b/test/EntityFramework.SqlServer.Tests/Metadata/InternalSqlServerMetadataBuilderExtensionsTest.cs index 2ed2655bcec..6c7fc0388f5 100644 --- a/test/EntityFramework.SqlServer.Tests/Metadata/InternalSqlServerMetadataBuilderExtensionsTest.cs +++ b/test/EntityFramework.SqlServer.Tests/Metadata/InternalSqlServerMetadataBuilderExtensionsTest.cs @@ -22,13 +22,13 @@ public void Can_access_model() { var builder = CreateBuilder(); - builder.SqlServer(ConfigurationSource.Convention).IdentityStrategy = SqlServerIdentityStrategy.SequenceHiLo; + Assert.True(builder.SqlServer(ConfigurationSource.Convention).IdentityStrategy(SqlServerIdentityStrategy.SequenceHiLo)); Assert.Equal(SqlServerIdentityStrategy.SequenceHiLo, builder.Metadata.SqlServer().IdentityStrategy); - builder.SqlServer(ConfigurationSource.DataAnnotation).IdentityStrategy = SqlServerIdentityStrategy.IdentityColumn; + Assert.True(builder.SqlServer(ConfigurationSource.DataAnnotation).IdentityStrategy(SqlServerIdentityStrategy.IdentityColumn)); Assert.Equal(SqlServerIdentityStrategy.IdentityColumn, builder.Metadata.SqlServer().IdentityStrategy); - builder.SqlServer(ConfigurationSource.Convention).IdentityStrategy = SqlServerIdentityStrategy.SequenceHiLo; + Assert.False(builder.SqlServer(ConfigurationSource.Convention).IdentityStrategy(SqlServerIdentityStrategy.SequenceHiLo)); Assert.Equal(SqlServerIdentityStrategy.IdentityColumn, builder.Metadata.SqlServer().IdentityStrategy); Assert.Equal(1, builder.Metadata.Annotations.Count( @@ -40,13 +40,13 @@ public void Can_access_entity_type() { var typeBuilder = CreateBuilder().Entity(typeof(Splot), ConfigurationSource.Convention); - typeBuilder.SqlServer(ConfigurationSource.Convention).TableName = "Splew"; + Assert.True(typeBuilder.SqlServer(ConfigurationSource.Convention).ToTable("Splew")); Assert.Equal("Splew", typeBuilder.Metadata.SqlServer().TableName); - typeBuilder.SqlServer(ConfigurationSource.DataAnnotation).TableName = "Splow"; + Assert.True(typeBuilder.SqlServer(ConfigurationSource.DataAnnotation).ToTable("Splow")); Assert.Equal("Splow", typeBuilder.Metadata.SqlServer().TableName); - typeBuilder.SqlServer(ConfigurationSource.Convention).TableName = "Splod"; + Assert.False(typeBuilder.SqlServer(ConfigurationSource.Convention).ToTable("Splod")); Assert.Equal("Splow", typeBuilder.Metadata.SqlServer().TableName); Assert.Equal(1, typeBuilder.Metadata.Annotations.Count( @@ -60,13 +60,13 @@ public void Can_access_property() .Entity(typeof(Splot), ConfigurationSource.Convention) .Property("Id", typeof(int), ConfigurationSource.Convention); - propertyBuilder.SqlServer(ConfigurationSource.Convention).HiLoSequenceName = "Splew"; + Assert.True(propertyBuilder.SqlServer(ConfigurationSource.Convention).HiLoSequenceName("Splew")); Assert.Equal("Splew", propertyBuilder.Metadata.SqlServer().HiLoSequenceName); - propertyBuilder.SqlServer(ConfigurationSource.DataAnnotation).HiLoSequenceName = "Splow"; + Assert.True(propertyBuilder.SqlServer(ConfigurationSource.DataAnnotation).HiLoSequenceName("Splow")); Assert.Equal("Splow", propertyBuilder.Metadata.SqlServer().HiLoSequenceName); - propertyBuilder.SqlServer(ConfigurationSource.Convention).HiLoSequenceName = "Splod"; + Assert.False(propertyBuilder.SqlServer(ConfigurationSource.Convention).HiLoSequenceName("Splod")); Assert.Equal("Splow", propertyBuilder.Metadata.SqlServer().HiLoSequenceName); Assert.Equal(1, propertyBuilder.Metadata.Annotations.Count( @@ -81,13 +81,13 @@ public void Can_access_key() var property = entityTypeBuilder.Property("Id", typeof(int), ConfigurationSource.Convention).Metadata; var keyBuilder = entityTypeBuilder.Key(new[] { property }, ConfigurationSource.Convention); - keyBuilder.SqlServer(ConfigurationSource.Convention).IsClustered = true; + Assert.True(keyBuilder.SqlServer(ConfigurationSource.Convention).Clustered(true)); Assert.True(keyBuilder.Metadata.SqlServer().IsClustered); - keyBuilder.SqlServer(ConfigurationSource.DataAnnotation).IsClustered = false; + Assert.True(keyBuilder.SqlServer(ConfigurationSource.DataAnnotation).Clustered(false)); Assert.False(keyBuilder.Metadata.SqlServer().IsClustered); - keyBuilder.SqlServer(ConfigurationSource.Convention).IsClustered = true; + Assert.False(keyBuilder.SqlServer(ConfigurationSource.Convention).Clustered(true)); Assert.False(keyBuilder.Metadata.SqlServer().IsClustered); Assert.Equal(1, keyBuilder.Metadata.Annotations.Count( @@ -102,13 +102,13 @@ public void Can_access_index() entityTypeBuilder.Property("Id", typeof(int), ConfigurationSource.Convention); var indexBuilder = entityTypeBuilder.Index(new[] { "Id" }, ConfigurationSource.Convention); - indexBuilder.SqlServer(ConfigurationSource.Convention).IsClustered = true; + Assert.True(indexBuilder.SqlServer(ConfigurationSource.Convention).Clustered(true)); Assert.True(indexBuilder.Metadata.SqlServer().IsClustered); - indexBuilder.SqlServer(ConfigurationSource.DataAnnotation).IsClustered = false; + Assert.True(indexBuilder.SqlServer(ConfigurationSource.DataAnnotation).Clustered(false)); Assert.False(indexBuilder.Metadata.SqlServer().IsClustered); - indexBuilder.SqlServer(ConfigurationSource.Convention).IsClustered = true; + Assert.False(indexBuilder.SqlServer(ConfigurationSource.Convention).Clustered(true)); Assert.False(indexBuilder.Metadata.SqlServer().IsClustered); Assert.Equal(1, indexBuilder.Metadata.Annotations.Count( @@ -123,13 +123,13 @@ public void Can_access_relationship() entityTypeBuilder.Property("Id", typeof(int), ConfigurationSource.Convention); var relationshipBuilder = entityTypeBuilder.ForeignKey("Splot", new[] { "Id" }, ConfigurationSource.Convention); - relationshipBuilder.SqlServer(ConfigurationSource.Convention).Name = "Splew"; + Assert.True(relationshipBuilder.SqlServer(ConfigurationSource.Convention).Name("Splew")); Assert.Equal("Splew", relationshipBuilder.Metadata.SqlServer().Name); - relationshipBuilder.SqlServer(ConfigurationSource.DataAnnotation).Name = "Splow"; + Assert.True(relationshipBuilder.SqlServer(ConfigurationSource.DataAnnotation).Name("Splow")); Assert.Equal("Splow", relationshipBuilder.Metadata.SqlServer().Name); - relationshipBuilder.SqlServer(ConfigurationSource.Convention).Name = "Splod"; + Assert.False(relationshipBuilder.SqlServer(ConfigurationSource.Convention).Name("Splod")); Assert.Equal("Splow", relationshipBuilder.Metadata.SqlServer().Name); Assert.Equal(1, relationshipBuilder.Metadata.Annotations.Count( diff --git a/test/EntityFramework.Sqlite.Design.FunctionalTests/SqliteMetadataModelProviderTest.cs b/test/EntityFramework.Sqlite.Design.FunctionalTests/SqliteMetadataModelProviderTest.cs index 6f8bf83a5fb..8fc81f7eb8b 100644 --- a/test/EntityFramework.Sqlite.Design.FunctionalTests/SqliteMetadataModelProviderTest.cs +++ b/test/EntityFramework.Sqlite.Design.FunctionalTests/SqliteMetadataModelProviderTest.cs @@ -157,7 +157,7 @@ FOREIGN KEY (parentid) REFERENCES parent (id) var parent = model.GetEntityType("Parent"); var children = model.GetEntityType("Children"); - Assert.NotEmpty(parent.GetReferencingForeignKeys()); + Assert.NotEmpty(parent.FindReferencingForeignKeys()); Assert.NotEmpty(children.GetForeignKeys()); var principalKey = children.GetForeignKey(children.FindProperty("ParentId")).PrincipalKey; @@ -180,7 +180,7 @@ FOREIGN KEY (parentid_A, parentid_B) REFERENCES parent (id_a, id_b) var parent = model.GetEntityType("Parent"); var children = model.GetEntityType("Children"); - Assert.NotEmpty(parent.GetReferencingForeignKeys()); + Assert.NotEmpty(parent.FindReferencingForeignKeys()); Assert.NotEmpty(children.GetForeignKeys()); var propList = new List @@ -206,7 +206,7 @@ FOREIGN KEY (ParentId) REFERENCES ItemsList (Id) var model = GetModel(sql); var list = model.GetEntityType("ItemsList"); - Assert.NotEmpty(list.GetReferencingForeignKeys()); + Assert.NotEmpty(list.FindReferencingForeignKeys()); Assert.NotEmpty(list.GetForeignKeys()); var principalKey = list.GetForeignKey(list.FindProperty("ParentId")).PrincipalKey; diff --git a/test/EntityFramework.Sqlite.FunctionalTests/InheritanceSqliteFixture.cs b/test/EntityFramework.Sqlite.FunctionalTests/InheritanceSqliteFixture.cs index b884f029db0..27a49f99180 100644 --- a/test/EntityFramework.Sqlite.FunctionalTests/InheritanceSqliteFixture.cs +++ b/test/EntityFramework.Sqlite.FunctionalTests/InheritanceSqliteFixture.cs @@ -61,7 +61,10 @@ CREATE TABLE Plant ( Genus int NOT NULL, Species nvarchar(100) NOT NULL PRIMARY KEY, Name nvarchar(100) NOT NULL, - HasThorns bit + CountryId int, + HasThorns bit, + + FOREIGN KEY(countryId) REFERENCES Country(Id) ); "); diff --git a/test/EntityFramework.Sqlite.FunctionalTests/InheritanceSqliteTest.cs b/test/EntityFramework.Sqlite.FunctionalTests/InheritanceSqliteTest.cs index 1f4d8d78264..92f398531fd 100644 --- a/test/EntityFramework.Sqlite.FunctionalTests/InheritanceSqliteTest.cs +++ b/test/EntityFramework.Sqlite.FunctionalTests/InheritanceSqliteTest.cs @@ -61,7 +61,7 @@ public override void Can_use_of_type_rose() base.Can_use_of_type_rose(); Assert.Equal( - @"SELECT ""p"".""Species"", ""p"".""Genus"", ""p"".""Name"", ""p"".""HasThorns"" + @"SELECT ""p"".""Species"", ""p"".""CountryId"", ""p"".""Genus"", ""p"".""Name"", ""p"".""HasThorns"" FROM ""Plant"" AS ""p"" WHERE ""p"".""Genus"" = 0", Sql); @@ -84,7 +84,7 @@ public override void Can_query_all_plants() base.Can_query_all_plants(); Assert.Equal( - @"SELECT ""a"".""Species"", ""a"".""Genus"", ""a"".""Name"", ""a"".""HasThorns"" + @"SELECT ""a"".""Species"", ""a"".""CountryId"", ""a"".""Genus"", ""a"".""Name"", ""a"".""HasThorns"" FROM ""Plant"" AS ""a"" WHERE ""a"".""Genus"" IN (0, 1) ORDER BY ""a"".""Species""", @@ -132,7 +132,7 @@ public override void Can_query_just_roses() base.Can_query_just_roses(); Assert.Equal( - @"SELECT ""p"".""Species"", ""p"".""Genus"", ""p"".""Name"", ""p"".""HasThorns"" + @"SELECT ""p"".""Species"", ""p"".""CountryId"", ""p"".""Genus"", ""p"".""Name"", ""p"".""HasThorns"" FROM ""Plant"" AS ""p"" WHERE ""p"".""Genus"" = 0 LIMIT 2", diff --git a/test/EntityFramework.Sqlite.Tests/Metadata/InternalSqliteMetadataBuilderExtensionsTest.cs b/test/EntityFramework.Sqlite.Tests/Metadata/InternalSqliteMetadataBuilderExtensionsTest.cs index 14d22726347..90374baec5d 100644 --- a/test/EntityFramework.Sqlite.Tests/Metadata/InternalSqliteMetadataBuilderExtensionsTest.cs +++ b/test/EntityFramework.Sqlite.Tests/Metadata/InternalSqliteMetadataBuilderExtensionsTest.cs @@ -34,13 +34,13 @@ public void Can_access_entity_type() { var typeBuilder = CreateBuilder().Entity(typeof(Splot), ConfigurationSource.Convention); - typeBuilder.Sqlite(ConfigurationSource.Convention).TableName = "Splew"; + Assert.True(typeBuilder.Sqlite(ConfigurationSource.Convention).ToTable("Splew")); Assert.Equal("Splew", typeBuilder.Metadata.Sqlite().TableName); - typeBuilder.Sqlite(ConfigurationSource.DataAnnotation).TableName = "Splow"; + Assert.True(typeBuilder.Sqlite(ConfigurationSource.DataAnnotation).ToTable("Splow")); Assert.Equal("Splow", typeBuilder.Metadata.Sqlite().TableName); - typeBuilder.Sqlite(ConfigurationSource.Convention).TableName = "Splod"; + Assert.False(typeBuilder.Sqlite(ConfigurationSource.Convention).ToTable("Splod")); Assert.Equal("Splow", typeBuilder.Metadata.Sqlite().TableName); Assert.Equal(1, typeBuilder.Metadata.Annotations.Count( @@ -54,13 +54,13 @@ public void Can_access_property() .Entity(typeof(Splot), ConfigurationSource.Convention) .Property("Id", typeof(int), ConfigurationSource.Convention); - propertyBuilder.Sqlite(ConfigurationSource.Convention).ColumnName = "Splew"; + Assert.True(propertyBuilder.Sqlite(ConfigurationSource.Convention).ColumnName("Splew")); Assert.Equal("Splew", propertyBuilder.Metadata.Sqlite().ColumnName); - propertyBuilder.Sqlite(ConfigurationSource.DataAnnotation).ColumnName = "Splow"; + Assert.True(propertyBuilder.Sqlite(ConfigurationSource.DataAnnotation).ColumnName("Splow")); Assert.Equal("Splow", propertyBuilder.Metadata.Sqlite().ColumnName); - propertyBuilder.Sqlite(ConfigurationSource.Convention).ColumnName = "Splod"; + Assert.False(propertyBuilder.Sqlite(ConfigurationSource.Convention).ColumnName("Splod")); Assert.Equal("Splow", propertyBuilder.Metadata.Sqlite().ColumnName); Assert.Equal(1, propertyBuilder.Metadata.Annotations.Count( @@ -75,13 +75,13 @@ public void Can_access_key() var property = entityTypeBuilder.Property("Id", typeof(int), ConfigurationSource.Convention).Metadata; var keyBuilder = entityTypeBuilder.Key(new[] { property }, ConfigurationSource.Convention); - keyBuilder.Sqlite(ConfigurationSource.Convention).Name = "Splew"; + Assert.True(keyBuilder.Sqlite(ConfigurationSource.Convention).Name("Splew")); Assert.Equal("Splew", keyBuilder.Metadata.Sqlite().Name); - keyBuilder.Sqlite(ConfigurationSource.DataAnnotation).Name = "Splow"; + Assert.True(keyBuilder.Sqlite(ConfigurationSource.DataAnnotation).Name("Splow")); Assert.Equal("Splow", keyBuilder.Metadata.Sqlite().Name); - keyBuilder.Sqlite(ConfigurationSource.Convention).Name = "Splod"; + Assert.False(keyBuilder.Sqlite(ConfigurationSource.Convention).Name("Splod")); Assert.Equal("Splow", keyBuilder.Metadata.Sqlite().Name); Assert.Equal(1, keyBuilder.Metadata.Annotations.Count( @@ -96,13 +96,13 @@ public void Can_access_index() entityTypeBuilder.Property("Id", typeof(int), ConfigurationSource.Convention); var indexBuilder = entityTypeBuilder.Index(new[] { "Id" }, ConfigurationSource.Convention); - indexBuilder.Sqlite(ConfigurationSource.Convention).Name = "Splew"; + indexBuilder.Sqlite(ConfigurationSource.Convention).Name("Splew"); Assert.Equal("Splew", indexBuilder.Metadata.Sqlite().Name); - indexBuilder.Sqlite(ConfigurationSource.DataAnnotation).Name = "Splow"; + indexBuilder.Sqlite(ConfigurationSource.DataAnnotation).Name("Splow"); Assert.Equal("Splow", indexBuilder.Metadata.Sqlite().Name); - indexBuilder.Sqlite(ConfigurationSource.Convention).Name = "Splod"; + indexBuilder.Sqlite(ConfigurationSource.Convention).Name("Splod"); Assert.Equal("Splow", indexBuilder.Metadata.Sqlite().Name); Assert.Equal(1, indexBuilder.Metadata.Annotations.Count( @@ -117,13 +117,13 @@ public void Can_access_relationship() entityTypeBuilder.Property("Id", typeof(int), ConfigurationSource.Convention); var relationshipBuilder = entityTypeBuilder.ForeignKey("Splot", new[] { "Id" }, ConfigurationSource.Convention); - relationshipBuilder.Sqlite(ConfigurationSource.Convention).Name = "Splew"; + Assert.True(relationshipBuilder.Sqlite(ConfigurationSource.Convention).Name("Splew")); Assert.Equal("Splew", relationshipBuilder.Metadata.Sqlite().Name); - relationshipBuilder.Sqlite(ConfigurationSource.DataAnnotation).Name = "Splow"; + Assert.True(relationshipBuilder.Sqlite(ConfigurationSource.DataAnnotation).Name("Splow")); Assert.Equal("Splow", relationshipBuilder.Metadata.Sqlite().Name); - relationshipBuilder.Sqlite(ConfigurationSource.Convention).Name = "Splod"; + Assert.False(relationshipBuilder.Sqlite(ConfigurationSource.Convention).Name("Splod")); Assert.Equal("Splow", relationshipBuilder.Metadata.Sqlite().Name); Assert.Equal(1, relationshipBuilder.Metadata.Annotations.Count(