using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Globalization; using System.Linq; using EFCore.NamingConventions.Internal; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; using Microsoft.EntityFrameworkCore.TestUtilities; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace EFCore.NamingConventions.Test; public class NameRewritingConventionTest { [Fact] public void Table() { var entityType = BuildEntityType(b => b.Entity()); Assert.Equal("sample_entity", entityType.GetTableName()); } [Fact] public void Column() { var entityType = BuildEntityType(b => b.Entity()); Assert.Equal("sample_entity_id", entityType.FindProperty(nameof(SampleEntity.SampleEntityId)) .GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value)); } [Fact] public void Column_with_turkish_culture() { var entityType = BuildEntityType( b => b.Entity(), CultureInfo.CreateSpecificCulture("tr-TR")); Assert.Equal("sample_entity_ıd", entityType.FindProperty(nameof(SampleEntity.SampleEntityId)) .GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value)); } [Fact] public void Column_with_invariant_culture() { var entityType = BuildEntityType( b => b.Entity(), CultureInfo.InvariantCulture); Assert.Equal("sample_entity_id", entityType.FindProperty(nameof(SampleEntity.SampleEntityId)) .GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value)); } [Fact] public void Column_on_view() { var entityType = BuildEntityType(b => b.Entity( e => { e.ToTable("SimpleBlogTable"); e.ToView("SimpleBlogView"); e.ToFunction("SimpleBlogFunction"); })); foreach (var type in new[] { StoreObjectType.Table, StoreObjectType.View, StoreObjectType.Function }) { Assert.Equal("sample_entity_id", entityType.FindProperty(nameof(SampleEntity.SampleEntityId)) .GetColumnName(StoreObjectIdentifier.Create(entityType, type)!.Value)); } } [Fact] public void Primary_key() { var entityType = BuildEntityType(b => b.Entity()); Assert.Equal("pk_sample_entity", entityType.FindPrimaryKey()!.GetName()); } [Fact] public void Primary_key_on_table_with_explicit_name() { var entityType = BuildEntityType(b => b.Entity().ToTable("some_explicit_name")); Assert.Equal("pk_some_explicit_name", entityType.FindPrimaryKey()!.GetName()); } [Fact] public void Alternative_key() { var entityType = BuildEntityType( b => b.Entity( e => { e.Property("SomeAlternateKey"); e.HasAlternateKey("SomeAlternateKey"); })); Assert.Equal("ak_sample_entity_some_alternate_key", entityType.GetKeys().Single(k => !k.IsPrimaryKey()).GetName()); } [Fact] public void Foreign_key() { var model = BuildModel(b => b.Entity()); var entityType = model.FindEntityType(typeof(Post)); Assert.Equal("fk_post_blog_blog_id", entityType.GetForeignKeys().Single().GetConstraintName()); } [Fact] public void Index() { var entityType = BuildEntityType(b => b.Entity().HasIndex(s => s.SomeProperty)); Assert.Equal("ix_sample_entity_some_property", entityType.GetIndexes().Single().GetDatabaseName()); } [Fact] public void TPH() { var model = BuildModel(b => { b.Entity(); b.Entity(); }); var parentEntityType = model.FindEntityType(typeof(Parent)); var childEntityType = model.FindEntityType(typeof(Child)); Assert.Equal("parent", parentEntityType.GetTableName()); Assert.Equal("id", parentEntityType.FindProperty(nameof(Parent.Id)) .GetColumnName(StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("parent_property", parentEntityType.FindProperty(nameof(Parent.ParentProperty)) .GetColumnName(StoreObjectIdentifier.Create(childEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("parent", childEntityType.GetTableName()); Assert.Equal("child_property", childEntityType.FindProperty(nameof(Child.ChildProperty)) .GetColumnName(StoreObjectIdentifier.Create(childEntityType, StoreObjectType.Table)!.Value)); Assert.Same(parentEntityType.FindPrimaryKey(), childEntityType.FindPrimaryKey()); } [Fact] public void TPT() { var model = BuildModel(b => { b.Entity().ToTable("parent"); b.Entity().ToTable("child"); }); var parentEntityType = model.FindEntityType(typeof(Parent)); var childEntityType = model.FindEntityType(typeof(Child)); Assert.Equal("parent", parentEntityType.GetTableName()); Assert.Equal("id", parentEntityType.FindProperty(nameof(Parent.Id)) .GetColumnName(StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("parent_property", parentEntityType.FindProperty(nameof(Parent.ParentProperty)) .GetColumnName(StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("child", childEntityType.GetTableName()); Assert.Equal("child_property", childEntityType.FindProperty(nameof(Child.ChildProperty)) .GetColumnName(StoreObjectIdentifier.Create(childEntityType, StoreObjectType.Table)!.Value)); var primaryKey = parentEntityType.FindPrimaryKey(); Assert.Same(primaryKey, childEntityType.FindPrimaryKey()); Assert.Equal("PK_parent", primaryKey.GetName()); // For the following, see #112 var parentStoreObjectIdentifier = StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table).Value; var childStoreObjectIdentifier = StoreObjectIdentifier.Create(childEntityType, StoreObjectType.Table).Value; Assert.Equal("PK_parent", primaryKey.GetName(parentStoreObjectIdentifier)); Assert.Equal("PK_child", primaryKey.GetName(childStoreObjectIdentifier)); } [Fact] public void TPT_reversed_configuration() { var model = BuildModel(b => { b.Entity().ToTable("child"); b.Entity().ToTable("parent"); }); var parentEntityType = model.FindEntityType(typeof(Parent)); var childEntityType = model.FindEntityType(typeof(Child)); Assert.Equal("parent", parentEntityType.GetTableName()); Assert.Equal("id", parentEntityType.FindProperty(nameof(Parent.Id)) .GetColumnName(StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("parent_property", parentEntityType.FindProperty(nameof(Parent.ParentProperty)) .GetColumnName(StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("child", childEntityType.GetTableName()); Assert.Equal("child_property", childEntityType.FindProperty(nameof(Child.ChildProperty)) .GetColumnName(StoreObjectIdentifier.Create(childEntityType, StoreObjectType.Table)!.Value)); var primaryKey = parentEntityType.FindPrimaryKey(); Assert.Same(primaryKey, childEntityType.FindPrimaryKey()); Assert.Equal("PK_parent", primaryKey.GetName()); // For the following, see #112 var parentStoreObjectIdentifier = StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table).Value; var childStoreObjectIdentifier = StoreObjectIdentifier.Create(childEntityType, StoreObjectType.Table).Value; Assert.Equal("PK_parent", primaryKey.GetName(parentStoreObjectIdentifier)); Assert.Equal("PK_child", primaryKey.GetName(childStoreObjectIdentifier)); } [Fact] public void TPH_with_owned() { var model = BuildModel(b => { b.Entity(); b.Entity().OwnsOne(c => c.Owned); }); var parentEntityType = model.FindEntityType(typeof(Parent)); var childEntityType = model.FindEntityType(typeof(ChildWithOwned)); var ownedEntityType = model.FindEntityType(typeof(Owned)); Assert.Equal("parent", parentEntityType.GetTableName()); Assert.Equal("id", parentEntityType.FindProperty(nameof(Parent.Id)) .GetColumnName(StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("parent_property", parentEntityType.FindProperty(nameof(Parent.ParentProperty)) .GetColumnName(StoreObjectIdentifier.Create(childEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("parent", childEntityType.GetTableName()); Assert.Equal("child_property", childEntityType.FindProperty(nameof(Child.ChildProperty)) .GetColumnName(StoreObjectIdentifier.Create(childEntityType, StoreObjectType.Table)!.Value)); Assert.Same(parentEntityType.FindPrimaryKey(), childEntityType.FindPrimaryKey()); Assert.Equal("parent", ownedEntityType.GetTableName()); Assert.Equal("owned_owned_property", ownedEntityType.FindProperty(nameof(Owned.OwnedProperty)) .GetColumnName(StoreObjectIdentifier.Create(ownedEntityType, StoreObjectType.Table)!.Value)); } [Fact] public void TPT_with_owned() { var model = BuildModel(b => { b.Entity().ToTable("parent"); b.Entity( e => { e.ToTable("child"); e.OwnsOne(c => c.Owned); }); }); var parentEntityType = model.FindEntityType(typeof(Parent)); var childEntityType = model.FindEntityType(typeof(ChildWithOwned)); var ownedEntityType = model.FindEntityType(typeof(Owned)); Assert.Equal("parent", parentEntityType.GetTableName()); Assert.Equal("id", parentEntityType.FindProperty(nameof(Parent.Id)) .GetColumnName(StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("parent_property", parentEntityType.FindProperty(nameof(Parent.ParentProperty)) .GetColumnName(StoreObjectIdentifier.Create(parentEntityType, StoreObjectType.Table)!.Value)); Assert.Equal("child", childEntityType.GetTableName()); Assert.Equal("child_property", childEntityType.FindProperty(nameof(ChildWithOwned.ChildProperty)) .GetColumnName(StoreObjectIdentifier.Create(childEntityType, StoreObjectType.Table)!.Value)); var parentKey = parentEntityType.FindPrimaryKey(); var childKey = childEntityType.FindPrimaryKey(); Assert.Equal("PK_parent", parentKey.GetName()); Assert.Equal("PK_parent", childKey.GetName()); Assert.Equal("child", ownedEntityType.GetTableName()); Assert.Equal("owned_owned_property", ownedEntityType.FindProperty(nameof(Owned.OwnedProperty)) .GetColumnName(StoreObjectIdentifier.Create(ownedEntityType, StoreObjectType.Table)!.Value)); } [Fact] public void Table_splitting1() { var model = BuildModel(b => { b.Entity( e => { e.ToTable("split_table"); e.HasOne(s1 => s1.S2).WithOne(s2 => s2.S1).HasForeignKey(s2 => s2.Id); }); b.Entity().ToTable("split_table"); }); var split1EntityType = model.FindEntityType(typeof(Split1)); var split2EntityType = model.FindEntityType(typeof(Split2)); var table = StoreObjectIdentifier.Create(split1EntityType, StoreObjectType.Table)!.Value; Assert.Equal(table, StoreObjectIdentifier.Create(split2EntityType, StoreObjectType.Table)); Assert.Equal("split_table", split1EntityType.GetTableName()); Assert.Equal("one_prop", split1EntityType.FindProperty(nameof(Split1.OneProp)).GetColumnName(table)); Assert.Equal("split_table", split2EntityType.GetTableName()); Assert.Equal("two_prop", split2EntityType.FindProperty(nameof(Split2.TwoProp)).GetColumnName(table)); Assert.Equal("common", split1EntityType.FindProperty(nameof(Split1.Common)).GetColumnName(table)); Assert.Equal("split2_common", split2EntityType.FindProperty(nameof(Split2.Common)).GetColumnName(table)); var foreignKey = split2EntityType.GetForeignKeys().Single(); Assert.Same(split1EntityType.FindPrimaryKey(), foreignKey.PrincipalKey); Assert.Same(split2EntityType.FindPrimaryKey().Properties.Single(), foreignKey.Properties.Single()); Assert.Equal(split1EntityType.FindPrimaryKey().GetName(), split2EntityType.FindPrimaryKey().GetName()); Assert.Equal( foreignKey.PrincipalKey.Properties.Single().GetColumnName(table), foreignKey.Properties.Single().GetColumnName(table)); Assert.Empty(split1EntityType.GetForeignKeys()); } [Fact] public void Table_splitting_and_explicit_owner_table() { var model = BuildModel(b => { b.Entity().HasOne(s1 => s1.S2).WithOne(s2 => s2.S1).HasForeignKey(s2 => s2.Id); b.Entity().ToTable("split_table"); b.Entity().ToTable("split_table"); }); var split1EntityType = model.FindEntityType(typeof(Split1)); var split2EntityType = model.FindEntityType(typeof(Split2)); var table = StoreObjectIdentifier.Create(split1EntityType, StoreObjectType.Table)!.Value; Assert.Equal(table, StoreObjectIdentifier.Create(split2EntityType, StoreObjectType.Table)); Assert.Equal("split_table", split1EntityType.GetTableName()); Assert.Equal("one_prop", split1EntityType.FindProperty(nameof(Split1.OneProp)).GetColumnName(table)); Assert.Equal("split_table", split2EntityType.GetTableName()); Assert.Equal("two_prop", split2EntityType.FindProperty(nameof(Split2.TwoProp)).GetColumnName(table)); Assert.Equal("common", split1EntityType.FindProperty(nameof(Split1.Common)).GetColumnName(table)); Assert.Equal("split2_common", split2EntityType.FindProperty(nameof(Split2.Common)).GetColumnName(table)); var foreignKey = split2EntityType.GetForeignKeys().Single(); Assert.Same(split1EntityType.FindPrimaryKey(), foreignKey.PrincipalKey); Assert.Same(split2EntityType.FindPrimaryKey().Properties.Single(), foreignKey.Properties.Single()); Assert.Equal(split1EntityType.FindPrimaryKey().GetName(), split2EntityType.FindPrimaryKey().GetName()); Assert.Equal( foreignKey.PrincipalKey.Properties.Single().GetColumnName(table), foreignKey.Properties.Single().GetColumnName(table)); Assert.Empty(split1EntityType.GetForeignKeys()); } [Fact] public void Owned_entity_with_table_splitting() { var model = BuildModel(b => b.Entity().OwnsOne(o => o.Owned)); var ownerEntityType = model.FindEntityType(typeof(Owner)); var ownedEntityType = model.FindEntityType(typeof(Owned)); Assert.Equal("owner", ownerEntityType.GetTableName()); Assert.Equal("owner", ownedEntityType.GetTableName()); var table = StoreObjectIdentifier.Create(ownerEntityType, StoreObjectType.Table)!.Value; Assert.Equal(table, StoreObjectIdentifier.Create(ownedEntityType, StoreObjectType.Table)!.Value); Assert.Equal("owned_owned_property", ownedEntityType.FindProperty(nameof(Owned.OwnedProperty)).GetColumnName(table)); var (ownerKey, ownedKey) = (ownerEntityType.FindPrimaryKey(), ownedEntityType.FindPrimaryKey()); Assert.Equal("pk_owner", ownerKey.GetName()); Assert.Equal("pk_owner", ownedKey.GetName()); Assert.Equal("id", ownerKey.Properties.Single().GetColumnName(table)); Assert.Equal("id", ownedKey.Properties.Single().GetColumnName(table)); } [Fact] public void Owned_entity_with_table_splitting_and_explicit_owner_table() { var model = BuildModel( b => b.Entity( e => { e.OwnsOne(o => o.Owned); e.ToTable("destination_table"); })); var ownerEntityType = model.FindEntityType(typeof(Owner)); var ownedEntityType = model.FindEntityType(typeof(Owned)); Assert.Equal("destination_table", ownerEntityType.GetTableName()); Assert.Equal("destination_table", ownedEntityType.GetTableName()); var table = StoreObjectIdentifier.Create(ownerEntityType, StoreObjectType.Table)!.Value; Assert.Equal(table, StoreObjectIdentifier.Create(ownedEntityType, StoreObjectType.Table)!.Value); Assert.Equal("owned_owned_property", ownedEntityType.FindProperty(nameof(Owned.OwnedProperty)).GetColumnName(table)); var (ownerKey, ownedKey) = (ownerEntityType.FindPrimaryKey(), ownedEntityType.FindPrimaryKey()); Assert.Equal("pk_destination_table", ownerKey.GetName()); Assert.Equal("pk_destination_table", ownedKey.GetName()); Assert.Equal("id", ownerKey.Properties.Single().GetColumnName(table)); Assert.Equal("id", ownedKey.Properties.Single().GetColumnName(table)); } [Fact] public void Owned_entity_twice_with_table_splitting_and_explicit_owner_table() { var model = BuildModel( b => b.Entity( e => { e.OwnsOne("owned1", o => o.Owned); e.OwnsOne("owned2", o => o.Owned2); e.ToTable("destination_table"); })); var ownerEntityType = model.FindEntityType(typeof(Owner)); var owned1EntityType = model.FindEntityType("owned1"); var owned2EntityType = model.FindEntityType("owned2"); Assert.Equal("destination_table", ownerEntityType.GetTableName()); Assert.Equal("destination_table", owned1EntityType.GetTableName()); Assert.Equal("destination_table", owned2EntityType.GetTableName()); var table = StoreObjectIdentifier.Create(ownerEntityType, StoreObjectType.Table)!.Value; Assert.Equal(table, StoreObjectIdentifier.Create(owned1EntityType, StoreObjectType.Table)!.Value); Assert.Equal(table, StoreObjectIdentifier.Create(owned2EntityType, StoreObjectType.Table)!.Value); Assert.Equal("owned_owned_property", owned1EntityType.FindProperty(nameof(Owned.OwnedProperty)).GetColumnName(table)); Assert.Equal("owned2_owned_property", owned2EntityType.FindProperty(nameof(Owned.OwnedProperty)).GetColumnName(table)); var (ownerKey, owned1Key, owned2Key) = (ownerEntityType.FindPrimaryKey(), owned1EntityType.FindPrimaryKey(), owned1EntityType.FindPrimaryKey()); Assert.Equal("pk_destination_table", ownerKey.GetName()); Assert.Equal("pk_destination_table", owned1Key.GetName()); Assert.Equal("pk_destination_table", owned2Key.GetName()); Assert.Equal("id", ownerKey.Properties.Single().GetColumnName(table)); Assert.Equal("id", owned1Key.Properties.Single().GetColumnName(table)); Assert.Equal("id", owned2Key.Properties.Single().GetColumnName(table)); } [Fact] public void Owned_entity_without_table_splitting() { var model = BuildModel(b => b.Entity().OwnsOne(o => o.Owned).ToTable("another_table")); var ownedEntityType = model.FindEntityType(typeof(Owned)); Assert.Equal("pk_another_table", ownedEntityType.FindPrimaryKey().GetName()); Assert.Equal("another_table", ownedEntityType.GetTableName()); Assert.Equal("owned_property", ownedEntityType.FindProperty("OwnedProperty") .GetColumnName(StoreObjectIdentifier.Create(ownedEntityType, StoreObjectType.Table)!.Value)); } [Fact] public void Owned_entity_withs_OwnsMany() { var model = BuildModel(b => b.Entity().OwnsMany(b => b.Posts)); var ownedEntityType = model.FindEntityType(typeof(Post)); Assert.Equal("post", ownedEntityType.GetTableName()); Assert.Equal("pk_post", ownedEntityType.FindPrimaryKey().GetName()); Assert.Equal("post_title", ownedEntityType.FindProperty("PostTitle") .GetColumnName(StoreObjectIdentifier.Create(ownedEntityType, StoreObjectType.Table)!.Value)); } [Fact] public void Not_mapped_to_table() { var entityType = BuildEntityType(b => b.Entity().ToSqlQuery("SELECT foobar")); Assert.Null(entityType.GetTableName()); } private IEntityType BuildEntityType(Action builderAction, CultureInfo culture = null) => BuildModel(builderAction, culture).GetEntityTypes().Single(); private IModel BuildModel(Action builderAction, CultureInfo culture = null) { var conventionSet = SqliteTestHelpers .Instance .CreateContextServices() .GetRequiredService() .CreateConventionSet(); var optionsBuilder = new DbContextOptionsBuilder(); SqliteTestHelpers.Instance.UseProviderOptions(optionsBuilder); optionsBuilder.UseSnakeCaseNamingConvention(culture); var plugin = new NamingConventionSetPlugin(optionsBuilder.Options); plugin.ModifyConventions(conventionSet); var modelBuilder = new ModelBuilder(conventionSet); builderAction(modelBuilder); return modelBuilder.FinalizeModel(); } public class SampleEntity { public int SampleEntityId { get; set; } public int SomeProperty { get; set; } } public class Blog { public int BlogId { get; set; } public List Posts { get; set; } } public class Post { public int PostId { get; set; } public Blog Blog { get; set; } public int BlogId { get; set; } public string PostTitle { get; set; } } public class Parent { public int Id { get; set; } public int ParentProperty { get; set; } } public class Child : Parent { public int ChildProperty { get; set; } } public class ChildWithOwned : Parent { public int ChildProperty { get; set; } public Owned Owned { get; set; } } public class Split1 { public int Id { get; set; } public int OneProp { get; set; } public int Common { get; set; } public Split2 S2 { get; set; } } public class Split2 { public int Id { get; set; } public int TwoProp { get; set; } public int Common { get; set; } public Split1 S1 { get; set; } } public class Owner { public int Id { get; set; } public int OwnerProperty { get; set; } public Owned Owned { get; set; } [NotMapped] public Owned Owned2 { get; set; } } public class Owned { public int OwnedProperty { get; set; } } }