mirror of
https://github.com/fergalmoran/EFCore.NamingConventions.git
synced 2025-12-22 01:28:13 +00:00
Fix owned entity support (#58)
* Also avoid rewriting primary key name with TPT * Also redo tests again to be properly end-to-end Fixes #50
This commit is contained in:
@@ -11,7 +11,6 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Specification.Tests" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Specification.Tests" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
|
||||
|
||||
@@ -1,103 +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 System;
|
||||
using System.Linq;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Xunit;
|
||||
|
||||
namespace EFCore.NamingConventions.Test
|
||||
{
|
||||
public class EndToEndTest : IClassFixture<EndToEndTest.EndToEndTestFixture>
|
||||
{
|
||||
public EndToEndTest(EndToEndTestFixture fixture)
|
||||
=> Fixture = fixture;
|
||||
|
||||
[Fact]
|
||||
public void Table_splitting()
|
||||
{
|
||||
using var context = CreateContext();
|
||||
|
||||
var split1EntityType = context.Model.FindEntityType(typeof(Split1));
|
||||
var split2EntityType = context.Model.FindEntityType(typeof(Split2));
|
||||
|
||||
var table = StoreObjectIdentifier.Create(split1EntityType, StoreObjectType.Table)!.Value;
|
||||
Assert.Equal(table, StoreObjectIdentifier.Create(split2EntityType, StoreObjectType.Table));
|
||||
|
||||
Assert.Equal("common", split1EntityType.FindProperty("Common").GetColumnName(table));
|
||||
Assert.Equal("split2_common", split2EntityType.FindProperty("Common").GetColumnName(table));
|
||||
|
||||
var split1 = context.Set<Split1>().Include(s1 => s1.Split2).Single();
|
||||
Assert.Equal(100, split1.Common);
|
||||
Assert.Equal(101, split1.Split2.Common);
|
||||
}
|
||||
|
||||
TestContext CreateContext() => Fixture.CreateContext();
|
||||
|
||||
readonly EndToEndTestFixture Fixture;
|
||||
|
||||
public class TestContext : DbContext
|
||||
{
|
||||
public TestContext(SqliteConnection connection)
|
||||
: base(new DbContextOptionsBuilder<TestContext>()
|
||||
.UseSqlite(connection)
|
||||
.UseSnakeCaseNamingConvention()
|
||||
.Options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Split1>(e =>
|
||||
{
|
||||
e.ToTable("split");
|
||||
e.HasOne(s1 => s1.Split2).WithOne(s2 => s2.Split1).HasForeignKey<Split2>(s2 => s2.Id);
|
||||
e.HasData(new Split1 { Id = 1, OneProp = 1, Common = 100 });
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Split2>(e =>
|
||||
{
|
||||
e.ToTable("split");
|
||||
e.HasData(new Split2 { Id = 1, TwoProp = 2, Common = 101 });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class Split1
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int OneProp { get; set; }
|
||||
public int Common { get; set; }
|
||||
|
||||
public Split2 Split2 { get; set; }
|
||||
}
|
||||
|
||||
public class Split2
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int TwoProp { get; set; }
|
||||
public int Common { get; set; }
|
||||
|
||||
public Split1 Split1 { get; set; }
|
||||
}
|
||||
|
||||
public class EndToEndTestFixture : IDisposable
|
||||
{
|
||||
private readonly SqliteConnection _connection;
|
||||
|
||||
public TestContext CreateContext() => new(_connection);
|
||||
|
||||
public EndToEndTestFixture()
|
||||
{
|
||||
_connection = new SqliteConnection("Filename=:memory:");
|
||||
_connection.Open();
|
||||
using var context = new TestContext(_connection);
|
||||
context.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
public void Dispose() => _connection.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,283 +1,131 @@
|
||||
using System;
|
||||
// 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.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using EFCore.NamingConventions.Internal;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
|
||||
using Microsoft.EntityFrameworkCore.TestUtilities;
|
||||
using Xunit;
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace EFCore.NamingConventions.Test
|
||||
{
|
||||
public class NameRewritingConventionTest
|
||||
{
|
||||
[Fact]
|
||||
public void Table_name()
|
||||
public void Table()
|
||||
{
|
||||
var entityType = BuildEntityType("SimpleBlog", _ => {});
|
||||
Assert.Equal("simple_blog", entityType.GetTableName());
|
||||
var entityType = BuildEntityType(b => b.Entity<SampleEntity>());
|
||||
Assert.Equal("sample_entity", entityType.GetTableName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_name()
|
||||
public void Column()
|
||||
{
|
||||
var entityType = BuildEntityType("SimpleBlog", e => e.Property<int>("SimpleBlogId"));
|
||||
|
||||
Assert.Equal("simple_blog_id", entityType.FindProperty("SimpleBlogId")
|
||||
var entityType = BuildEntityType(b => b.Entity<SampleEntity>());
|
||||
Assert.Equal("sample_entity_id", entityType.FindProperty(nameof(SampleEntity.SampleEntityId))
|
||||
.GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_name_on_view()
|
||||
public void Column_with_turkish_culture()
|
||||
{
|
||||
var entityType = BuildEntityType("SimpleBlog", e =>
|
||||
{
|
||||
e.ToTable("SimpleBlogTable");
|
||||
e.ToView("SimpleBlogView");
|
||||
e.ToFunction("SimpleBlogFunction");
|
||||
e.Property<int>("SimpleBlogId");
|
||||
});
|
||||
var entityType = BuildEntityType(
|
||||
b => b.Entity<SampleEntity>(),
|
||||
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<SampleEntity>(),
|
||||
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<SampleEntity>(
|
||||
e =>
|
||||
{
|
||||
e.ToTable("SimpleBlogTable");
|
||||
e.ToView("SimpleBlogView");
|
||||
e.ToFunction("SimpleBlogFunction");
|
||||
}));
|
||||
|
||||
foreach (var type in new[] { StoreObjectType.Table, StoreObjectType.View, StoreObjectType.Function })
|
||||
{
|
||||
Assert.Equal("simple_blog_id", entityType.FindProperty("SimpleBlogId")
|
||||
Assert.Equal("sample_entity_id", entityType.FindProperty(nameof(SampleEntity.SampleEntityId))
|
||||
.GetColumnName(StoreObjectIdentifier.Create(entityType, type)!.Value));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_name_turkish_culture()
|
||||
public void Primary_key()
|
||||
{
|
||||
var entityType = BuildEntityType(b => b.Entity<SampleEntity>());
|
||||
Assert.Equal("pk_sample_entity", entityType.GetKeys().Single(k => k.IsPrimaryKey()).GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alternative_key()
|
||||
{
|
||||
var entityType = BuildEntityType(
|
||||
"SimpleBlog",
|
||||
e => e.Property<int>("SimpleBlogId"),
|
||||
CultureInfo.CreateSpecificCulture("tr-TR"));
|
||||
|
||||
Assert.Equal("simple_blog_ıd", entityType.FindProperty("SimpleBlogId")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value));
|
||||
b => b.Entity<SampleEntity>(
|
||||
e =>
|
||||
{
|
||||
e.Property<int>("SomeAlternateKey");
|
||||
e.HasAlternateKey("SomeAlternateKey");
|
||||
}));
|
||||
Assert.Equal("ak_sample_entity_some_alternate_key", entityType.GetKeys().Single(k => !k.IsPrimaryKey()).GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Column_name_invariant_culture()
|
||||
public void Foreign_key()
|
||||
{
|
||||
var entityType = BuildEntityType(
|
||||
"SimpleBlog",
|
||||
e => e.Property<int>("SimpleBlogId"),
|
||||
CultureInfo.InvariantCulture);
|
||||
|
||||
Assert.Equal("simple_blog_id", entityType.FindProperty("SimpleBlogId")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Primary_key_name()
|
||||
{
|
||||
var entityType = BuildEntityType("SimpleBlog", e =>
|
||||
{
|
||||
e.Property<int>("SimpleBlogId");
|
||||
e.HasKey("SimpleBlogId");
|
||||
});
|
||||
|
||||
Assert.Equal("pk_simple_blog", entityType.GetKeys().Single(k => k.IsPrimaryKey()).GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Alternative_key_name()
|
||||
{
|
||||
var entityType = BuildEntityType("SimpleBlog", e =>
|
||||
{
|
||||
e.Property<int>("SimpleBlogId");
|
||||
e.Property<int>("SomeAlternativeKey");
|
||||
e.HasKey("SimpleBlogId");
|
||||
e.HasAlternateKey("SomeAlternativeKey");
|
||||
});
|
||||
|
||||
Assert.Equal("ak_simple_blog_some_alternative_key", entityType.GetKeys().Single(k => !k.IsPrimaryKey()).GetName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Foreign_key_name()
|
||||
{
|
||||
var model = BuildModel(b =>
|
||||
{
|
||||
b.Entity("Blog", e =>
|
||||
{
|
||||
e.Property<int>("BlogId");
|
||||
e.HasKey("BlogId");
|
||||
e.HasMany("Post").WithOne("Blog");
|
||||
});
|
||||
b.Entity("Post", e =>
|
||||
{
|
||||
e.Property<int>("PostId");
|
||||
e.Property<int>("BlogId");
|
||||
e.HasKey("PostId");
|
||||
});
|
||||
});
|
||||
var entityType = model.FindEntityType("Post");
|
||||
|
||||
var model = BuildModel(b => b.Entity<Blog>());
|
||||
var entityType = model.FindEntityType(typeof(Post));
|
||||
Assert.Equal("fk_post_blog_blog_id", entityType.GetForeignKeys().Single().GetConstraintName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Index_name()
|
||||
public void Index()
|
||||
{
|
||||
var entityType = BuildEntityType("SimpleBlog", e =>
|
||||
{
|
||||
e.Property<int>("IndexedProperty");
|
||||
e.HasIndex("IndexedProperty");
|
||||
});
|
||||
|
||||
Assert.Equal("ix_simple_blog_indexed_property", entityType.GetIndexes().Single().GetDatabaseName());
|
||||
var entityType = BuildEntityType(b => b.Entity<SampleEntity>().HasIndex(s => s.SomeProperty));
|
||||
Assert.Equal("ix_sample_entity_some_property", entityType.GetIndexes().Single().GetDatabaseName());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Table_splitting()
|
||||
{
|
||||
var model = BuildModel(b =>
|
||||
{
|
||||
b.Entity("One", e =>
|
||||
{
|
||||
e.ToTable("table");
|
||||
e.Property<int>("Id");
|
||||
e.Property<int>("OneProp");
|
||||
e.Property<int>("Common");
|
||||
|
||||
e.HasOne("Two").WithOne().HasForeignKey("Two", "Id");
|
||||
});
|
||||
|
||||
b.Entity("Two", e =>
|
||||
{
|
||||
e.ToTable("table");
|
||||
e.Property<int>("Id");
|
||||
e.Property<int>("TwoProp");
|
||||
e.Property<int>("Common");
|
||||
});
|
||||
});
|
||||
|
||||
var oneEntityType = model.FindEntityType("One");
|
||||
var twoEntityType = model.FindEntityType("Two");
|
||||
|
||||
var table = StoreObjectIdentifier.Create(oneEntityType, StoreObjectType.Table)!.Value;
|
||||
Assert.Equal(table, StoreObjectIdentifier.Create(twoEntityType, StoreObjectType.Table));
|
||||
|
||||
Assert.Equal("table", oneEntityType.GetTableName());
|
||||
Assert.Equal("one_prop", oneEntityType.FindProperty("OneProp").GetColumnName(table));
|
||||
|
||||
Assert.Equal("table", twoEntityType.GetTableName());
|
||||
Assert.Equal("two_prop", twoEntityType.FindProperty("TwoProp").GetColumnName(table));
|
||||
|
||||
var foreignKey = twoEntityType.GetForeignKeys().Single();
|
||||
Assert.Same(oneEntityType.FindPrimaryKey(), foreignKey.PrincipalKey);
|
||||
Assert.Same(twoEntityType.FindPrimaryKey().Properties.Single(), foreignKey.Properties.Single());
|
||||
Assert.Equal(oneEntityType.FindPrimaryKey().GetName(), twoEntityType.FindPrimaryKey().GetName());
|
||||
|
||||
Assert.Equal(
|
||||
foreignKey.PrincipalKey.Properties.Single().GetColumnName(table),
|
||||
foreignKey.Properties.Single().GetColumnName(table));
|
||||
|
||||
Assert.Empty(oneEntityType.GetForeignKeys());
|
||||
}
|
||||
|
||||
#region Owned entities
|
||||
|
||||
[Fact]
|
||||
public void Owned_entity_with_table_splitting()
|
||||
{
|
||||
var model = BuildModel(b =>
|
||||
{
|
||||
b.Entity("SimpleBlog", e =>
|
||||
{
|
||||
e.OwnsOne("OwnedEntity", "Nav", o => o.Property<int>("OwnedProperty"));
|
||||
});
|
||||
});
|
||||
|
||||
var entityType = model.FindEntityType("OwnedEntity");
|
||||
Assert.Equal("pk_simple_blog", entityType.FindPrimaryKey().GetName());
|
||||
Assert.Equal("simple_blog", entityType.GetTableName());
|
||||
Assert.Equal("owned_property", entityType.FindProperty("OwnedProperty")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Owned_entity_without_table_splitting()
|
||||
{
|
||||
var model = BuildModel(b =>
|
||||
{
|
||||
b.Entity("SimpleBlog", e =>
|
||||
{
|
||||
e.Property<int>("SimpleBlogId");
|
||||
e.HasKey("SimpleBlogId");
|
||||
e.OwnsOne("OwnedEntity", "Nav", o =>
|
||||
{
|
||||
o.ToTable("another_table");
|
||||
o.Property<int>("OwnedProperty");
|
||||
});
|
||||
});
|
||||
});
|
||||
var entityType = model.FindEntityType("OwnedEntity");
|
||||
|
||||
Assert.Equal("pk_another_table", entityType.FindPrimaryKey().GetName());
|
||||
Assert.Equal("another_table", entityType.GetTableName());
|
||||
Assert.Equal("owned_property", entityType.FindProperty("OwnedProperty")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Owned_entity_with_view_without_table_splitting()
|
||||
{
|
||||
var model = BuildModel(b =>
|
||||
{
|
||||
b.Entity("OwnedEntity", e =>
|
||||
{
|
||||
e.ToTable("OwnedEntityTable");
|
||||
e.ToView("OwnedEntityView");
|
||||
e.Property<int>("OwnedProperty");
|
||||
});
|
||||
b.Entity("SimpleBlog", e => e.OwnsOne("OwnedEntity", "Nav"));
|
||||
});
|
||||
var entityType = model.FindEntityType("OwnedEntity");
|
||||
|
||||
Assert.Equal("OwnedEntityTable", entityType.GetTableName());
|
||||
Assert.Equal("OwnedEntityView", entityType.GetViewName());
|
||||
Assert.Equal("owned_property", entityType.FindProperty("OwnedProperty")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.Table)!.Value));
|
||||
Assert.Equal("owned_property", entityType.FindProperty("OwnedProperty")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(entityType, StoreObjectType.View)!.Value));
|
||||
}
|
||||
|
||||
#endregion Owned entities
|
||||
|
||||
#region Inheritance
|
||||
|
||||
[Fact]
|
||||
public void TPH()
|
||||
{
|
||||
var model = BuildModel(b =>
|
||||
{
|
||||
b.Entity("SimpleBlog", e =>
|
||||
{
|
||||
e.Property<int>("SimpleBlogId");
|
||||
e.HasKey("SimpleBlogId");
|
||||
});
|
||||
b.Entity("FancyBlog", e =>
|
||||
{
|
||||
e.HasBaseType("SimpleBlog");
|
||||
e.Property<int>("FancyProperty");
|
||||
});
|
||||
b.Entity<Parent>();
|
||||
b.Entity<Child>();
|
||||
});
|
||||
|
||||
var simpleBlogEntityType = model.FindEntityType("SimpleBlog");
|
||||
Assert.Equal("simple_blog", simpleBlogEntityType.GetTableName());
|
||||
Assert.Equal("simple_blog_id", simpleBlogEntityType.FindProperty("SimpleBlogId")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(simpleBlogEntityType, StoreObjectType.Table)!.Value));
|
||||
var parentEntityType = model.FindEntityType(typeof(Parent));
|
||||
var childEntityType = model.FindEntityType(typeof(Child));
|
||||
|
||||
var fancyBlogEntityType = model.FindEntityType("FancyBlog");
|
||||
Assert.Equal("simple_blog", fancyBlogEntityType.GetTableName());
|
||||
Assert.Equal("fancy_property", fancyBlogEntityType.FindProperty("FancyProperty")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(fancyBlogEntityType, StoreObjectType.Table)!.Value));
|
||||
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]
|
||||
@@ -285,51 +133,182 @@ namespace EFCore.NamingConventions.Test
|
||||
{
|
||||
var model = BuildModel(b =>
|
||||
{
|
||||
b.Entity("SimpleBlog", e =>
|
||||
{
|
||||
e.Property<int>("SimpleBlogId");
|
||||
e.HasKey("SimpleBlogId");
|
||||
});
|
||||
b.Entity("FancyBlog", e =>
|
||||
{
|
||||
e.HasBaseType("SimpleBlog");
|
||||
e.ToTable("fancy_blog");
|
||||
e.Property<int>("FancyProperty");
|
||||
});
|
||||
b.Entity<Parent>().ToTable("parent");
|
||||
b.Entity<Child>().ToTable("child");
|
||||
});
|
||||
|
||||
var simpleBlogEntityType = model.FindEntityType("SimpleBlog");
|
||||
Assert.Equal("simple_blog", simpleBlogEntityType.GetTableName());
|
||||
Assert.Equal("simple_blog_id", simpleBlogEntityType.FindProperty("SimpleBlogId")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(simpleBlogEntityType, StoreObjectType.Table)!.Value));
|
||||
var parentEntityType = model.FindEntityType(typeof(Parent));
|
||||
var childEntityType = model.FindEntityType(typeof(Child));
|
||||
|
||||
var fancyBlogEntityType = model.FindEntityType("FancyBlog");
|
||||
Assert.Equal("fancy_blog", fancyBlogEntityType.GetTableName());
|
||||
Assert.Equal("fancy_property", fancyBlogEntityType.FindProperty("FancyProperty")
|
||||
.GetColumnName(StoreObjectIdentifier.Create(fancyBlogEntityType, StoreObjectType.Table)!.Value));
|
||||
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 parentKey = parentEntityType.FindPrimaryKey();
|
||||
var childKey = childEntityType.FindPrimaryKey();
|
||||
|
||||
Assert.Equal("PK_parent", parentKey.GetName());
|
||||
Assert.Equal("PK_parent", childKey.GetName());
|
||||
}
|
||||
|
||||
#endregion Inheritance
|
||||
|
||||
#region Support
|
||||
|
||||
private IModel BuildModel(Action<ModelBuilder> buildAction, CultureInfo cultureInfo = null)
|
||||
[Fact]
|
||||
public void Table_splitting()
|
||||
{
|
||||
var conventionSet = InMemoryTestHelpers.Instance.CreateConventionSetBuilder().CreateConventionSet();
|
||||
ConventionSet.Remove(conventionSet.ModelFinalizedConventions, typeof(ValidatingConvention));
|
||||
var model = BuildModel(b =>
|
||||
{
|
||||
b.Entity<Split1>(
|
||||
e =>
|
||||
{
|
||||
e.ToTable("split_table");
|
||||
e.HasOne(s1 => s1.S2).WithOne(s2 => s2.S1).HasForeignKey<Split2>(s2 => s2.Id);
|
||||
});
|
||||
|
||||
b.Entity<Split2>(e => e.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<Owner>().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_without_table_splitting()
|
||||
{
|
||||
var model = BuildModel(b =>
|
||||
b.Entity<Owner>().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));
|
||||
}
|
||||
|
||||
private IEntityType BuildEntityType(Action<ModelBuilder> builderAction, CultureInfo culture = null)
|
||||
=> BuildModel(builderAction, culture).GetEntityTypes().Single();
|
||||
|
||||
private IModel BuildModel(Action<ModelBuilder> builderAction, CultureInfo culture = null)
|
||||
{
|
||||
var conventionSet = SqliteTestHelpers.Instance.CreateConventionSetBuilder().CreateConventionSet();
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder();
|
||||
optionsBuilder.UseSnakeCaseNamingConvention(cultureInfo);
|
||||
new NamingConventionSetPlugin(optionsBuilder.Options).ModifyConventions(conventionSet);
|
||||
SqliteTestHelpers.Instance.UseProviderOptions(optionsBuilder);
|
||||
optionsBuilder.UseSnakeCaseNamingConvention(culture);
|
||||
var plugin = new NamingConventionSetPlugin(optionsBuilder.Options);
|
||||
plugin.ModifyConventions(conventionSet);
|
||||
|
||||
var builder = new ModelBuilder(conventionSet);
|
||||
buildAction(builder);
|
||||
return builder.FinalizeModel();
|
||||
var modelBuilder = new ModelBuilder(conventionSet);
|
||||
builderAction(modelBuilder);
|
||||
return modelBuilder.FinalizeModel();
|
||||
}
|
||||
|
||||
private IEntityType BuildEntityType(string entityTypeName, Action<EntityTypeBuilder> buildAction, CultureInfo cultureInfo = null)
|
||||
=> BuildModel(b => buildAction(b.Entity(entityTypeName)), cultureInfo).GetEntityTypes().Single();
|
||||
public class SampleEntity
|
||||
{
|
||||
public int SampleEntityId { get; set; }
|
||||
public int SomeProperty { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
public class Blog
|
||||
{
|
||||
public int BlogId { get; set; }
|
||||
public List<Post> Posts { get; set; }
|
||||
}
|
||||
|
||||
public class Post
|
||||
{
|
||||
public int PostId { get; set; }
|
||||
public Blog Blog { get; set; }
|
||||
public int BlogId { 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 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; }
|
||||
}
|
||||
|
||||
public class Owned
|
||||
{
|
||||
public int OwnedProperty { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
// 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 Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.InMemory.Diagnostics.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Sqlite.Diagnostics.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
#pragma warning disable EF1001
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.EntityFrameworkCore.TestUtilities
|
||||
{
|
||||
public class InMemoryTestHelpers : TestHelpers
|
||||
public class SqliteTestHelpers : TestHelpers
|
||||
{
|
||||
protected InMemoryTestHelpers()
|
||||
protected SqliteTestHelpers()
|
||||
{
|
||||
}
|
||||
|
||||
public static InMemoryTestHelpers Instance { get; } = new();
|
||||
public static SqliteTestHelpers Instance { get; } = new();
|
||||
|
||||
public override IServiceCollection AddProviderServices(IServiceCollection services)
|
||||
=> services.AddEntityFrameworkInMemoryDatabase();
|
||||
=> services.AddEntityFrameworkSqlite();
|
||||
|
||||
public override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder)
|
||||
=> optionsBuilder.UseInMemoryDatabase(nameof(InMemoryTestHelpers));
|
||||
=> optionsBuilder.UseSqlite(new SqliteConnection("Data Source=:memory:"));
|
||||
|
||||
public override LoggingDefinitions LoggingDefinitions { get; } = new InMemoryLoggingDefinitions();
|
||||
#pragma warning disable EF1001
|
||||
public override LoggingDefinitions LoggingDefinitions { get; } = new SqliteLoggingDefinitions();
|
||||
#pragma warning enable EF1001
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
|
||||
namespace EFCore.NamingConventions.Internal
|
||||
{
|
||||
@@ -66,25 +64,7 @@ namespace EFCore.NamingConventions.Internal
|
||||
public virtual void ProcessPropertyAdded(
|
||||
IConventionPropertyBuilder propertyBuilder,
|
||||
IConventionContext<IConventionPropertyBuilder> context)
|
||||
{
|
||||
var entityType = propertyBuilder.Metadata.DeclaringEntityType;
|
||||
var property = propertyBuilder.Metadata;
|
||||
|
||||
propertyBuilder.HasColumnName(_namingNameRewriter.RewriteName(property.GetColumnBaseName()));
|
||||
|
||||
foreach (var storeObjectType in _storeObjectTypes)
|
||||
{
|
||||
var identifier = StoreObjectIdentifier.Create(entityType, storeObjectType);
|
||||
if (identifier is null)
|
||||
continue;
|
||||
|
||||
if (property.GetColumnNameConfigurationSource(identifier.Value) == ConfigurationSource.Convention)
|
||||
{
|
||||
propertyBuilder.HasColumnName(
|
||||
_namingNameRewriter.RewriteName(property.GetColumnName(identifier.Value)), identifier.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
=> RewriteColumnName(propertyBuilder);
|
||||
|
||||
public void ProcessForeignKeyOwnershipChanged(IConventionForeignKeyBuilder relationshipBuilder, IConventionContext<bool?> context)
|
||||
{
|
||||
@@ -102,27 +82,11 @@ namespace EFCore.NamingConventions.Internal
|
||||
ownedEntityType.Builder.HasNoAnnotation(RelationalAnnotationNames.TableName);
|
||||
ownedEntityType.Builder.HasNoAnnotation(RelationalAnnotationNames.Schema);
|
||||
|
||||
// Also need to reset all primary key properties
|
||||
foreach (var keyProperty in ownedEntityType.FindPrimaryKey().Properties)
|
||||
// We've previously set rewritten column names when the entity was originally added (before becoming owned).
|
||||
// These need to be rewritten again to include the owner prefix.
|
||||
foreach (var property in ownedEntityType.GetProperties())
|
||||
{
|
||||
keyProperty.Builder.HasNoAnnotation(RelationalAnnotationNames.ColumnName);
|
||||
|
||||
foreach (var storeObjectType in _storeObjectTypes)
|
||||
{
|
||||
var identifier = StoreObjectIdentifier.Create(ownedEntityType, storeObjectType);
|
||||
if (identifier is null)
|
||||
continue;
|
||||
|
||||
if (keyProperty.GetColumnNameConfigurationSource(identifier.Value) == ConfigurationSource.Convention)
|
||||
{
|
||||
#pragma warning disable EF1001
|
||||
// TODO: Using internal APIs to remove the override
|
||||
var tableOverrides = (IDictionary<StoreObjectIdentifier, RelationalPropertyOverrides>)
|
||||
keyProperty[RelationalAnnotationNames.RelationalOverrides];
|
||||
tableOverrides.Remove(identifier.Value);
|
||||
#pragma warning restore EF1001
|
||||
}
|
||||
}
|
||||
RewriteColumnName(property.Builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,40 +99,50 @@ namespace EFCore.NamingConventions.Internal
|
||||
return;
|
||||
}
|
||||
|
||||
var entityType = entityTypeBuilder.Metadata;
|
||||
|
||||
// The table's name is changing - rewrite keys, index names
|
||||
if (entityTypeBuilder.Metadata.FindPrimaryKey() is IConventionKey primaryKey)
|
||||
if (entityType.FindPrimaryKey() is IConventionKey primaryKey)
|
||||
{
|
||||
primaryKey.Builder.HasName(_namingNameRewriter.RewriteName(primaryKey.GetDefaultName()));
|
||||
if (entityType.BaseType is not null
|
||||
&& entityType.GetTableName() != entityType.BaseType.GetTableName())
|
||||
{
|
||||
// It's not yet possible to set the PK name with TPT, see https://github.com/dotnet/efcore/issues/23444.
|
||||
primaryKey.Builder.HasNoAnnotation(RelationalAnnotationNames.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
primaryKey.Builder.HasName(_namingNameRewriter.RewriteName(primaryKey.GetDefaultName()));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var foreignKey in entityTypeBuilder.Metadata.GetForeignKeys())
|
||||
foreach (var foreignKey in entityType.GetForeignKeys())
|
||||
{
|
||||
foreignKey.Builder.HasConstraintName(_namingNameRewriter.RewriteName(foreignKey.GetDefaultName()));
|
||||
}
|
||||
|
||||
foreach (var index in entityTypeBuilder.Metadata.GetIndexes())
|
||||
foreach (var index in entityType.GetIndexes())
|
||||
{
|
||||
index.Builder.HasDatabaseName(_namingNameRewriter.RewriteName(index.GetDefaultDatabaseName()));
|
||||
}
|
||||
|
||||
if (oldAnnotation?.Value is null &&
|
||||
annotation?.Value is not null &&
|
||||
entityTypeBuilder.Metadata.FindOwnership() is IConventionForeignKey ownership &&
|
||||
if (annotation?.Value is not null &&
|
||||
entityType.FindOwnership() is IConventionForeignKey ownership &&
|
||||
(string)annotation.Value != ownership.PrincipalEntityType.GetTableName())
|
||||
{
|
||||
// An owned entity's table is being set explicitly - this is the trigger to undo table splitting (which is the default).
|
||||
|
||||
// When the entity became owned, we prefixed all of its properties - we must now undo that.
|
||||
foreach (var property in entityTypeBuilder.Metadata.GetProperties()
|
||||
.Except(entityTypeBuilder.Metadata.FindPrimaryKey().Properties)
|
||||
foreach (var property in entityType.GetProperties()
|
||||
.Except(entityType.FindPrimaryKey().Properties)
|
||||
.Where(p => p.Builder.CanSetColumnName(null)))
|
||||
{
|
||||
property.Builder.HasColumnName(_namingNameRewriter.RewriteName(property.GetDefaultColumnBaseName()));
|
||||
RewriteColumnName(property.Builder);
|
||||
}
|
||||
|
||||
// We previously rewrote the owned entity's primary key name, when the owned entity was still in table splitting.
|
||||
// Now that its getting its own table, rewrite the primary key constraint name again.
|
||||
if (entityTypeBuilder.Metadata.FindPrimaryKey() is IConventionKey key)
|
||||
if (entityType.FindPrimaryKey() is IConventionKey key)
|
||||
{
|
||||
key.Builder.HasName(_namingNameRewriter.RewriteName(key.GetDefaultName()));
|
||||
}
|
||||
@@ -224,5 +198,33 @@ namespace EFCore.NamingConventions.Internal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RewriteColumnName(IConventionPropertyBuilder propertyBuilder)
|
||||
{
|
||||
var property = propertyBuilder.Metadata;
|
||||
var entityType = property.DeclaringEntityType;
|
||||
|
||||
// Remove any previous setting of the column name we may have done, so we can get the default recalculated below.
|
||||
property.Builder.HasNoAnnotation(RelationalAnnotationNames.ColumnName);
|
||||
|
||||
// TODO: The following is a temporary hack. We should probably just always set the relational override below,
|
||||
// but https://github.com/dotnet/efcore/pull/23834
|
||||
#pragma warning disable 618
|
||||
propertyBuilder.HasColumnName(_namingNameRewriter.RewriteName(property.GetColumnName()));
|
||||
#pragma warning restore 618
|
||||
|
||||
foreach (var storeObjectType in _storeObjectTypes)
|
||||
{
|
||||
var identifier = StoreObjectIdentifier.Create(entityType, storeObjectType);
|
||||
if (identifier is null)
|
||||
continue;
|
||||
|
||||
if (property.GetColumnNameConfigurationSource(identifier.Value) == ConfigurationSource.Convention)
|
||||
{
|
||||
propertyBuilder.HasColumnName(
|
||||
_namingNameRewriter.RewriteName(property.GetColumnName(identifier.Value)), identifier.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user