diff --git a/EntityFramework.Explained/Schema/Relationships/UniDirectionalOneToMany/UniDirectionalOneToManyWithOneDbSet.cs b/EntityFramework.Explained/Schema/Relationships/UniDirectionalOneToMany/UniDirectionalOneToManyWithOneDbSet.cs new file mode 100644 index 0000000..69442b0 --- /dev/null +++ b/EntityFramework.Explained/Schema/Relationships/UniDirectionalOneToMany/UniDirectionalOneToManyWithOneDbSet.cs @@ -0,0 +1,96 @@ +using EntityFramework.Explained._Tools.TestContexts; +using Microsoft.EntityFrameworkCore; +using QuickPulse.Explains; +using QuickPulse.Explains.Text; + +namespace EntityFramework.Explained.Schema.Relationships.UniDirectionalOneToMany; + +[DocFile] +[DocContent( +@"Because the entity used in the `DbSet` has a collection of another entity type, the latter are mapped as well. +EF infers and includes related entities in the schema even when only one side is explicitly registered in the `DbContext`. +")] +public class UniDirectionalOneToManyWithOneDbSet +{ + [DocExample] + public class Post + { + public int Id { get; set; } + public Blog Blog { get; set; } = default!; + } + + [DocExample] + public class Blog + { + public int Id { get; set; } + } + + [Fact] + [DocContent("When using the following simple model of *one* `Blog` containing *many* `Posts`: ")] + [DocCodeExample(typeof(Blog))] + [DocCodeExample(typeof(Post))] + [DocContent("And then adding a `DbSet` to the `DbContext` EF generates the following ddl for Sql Server: \n")] + [DocContent("**Blog:**")] + [DocCodeExample(typeof(UniDirectionalOneToManyWithOneDbSet), nameof(ExpectedDdlScriptForBlog))] + [DocContent("**Post:**")] + [DocCodeExample(typeof(UniDirectionalOneToManyWithOneDbSet), nameof(ExpectedDdlScriptForPost))] + [DocContent("**Index:**")] + [DocCodeExample(typeof(UniDirectionalOneToManyWithOneDbSet), nameof(ExpectedDdlScriptForIndex))] + [DocContent("*Note:* No other mappings were added.")] + public void SqlServer() + { + using var context = new TestSqlServerContext(); + + var sql = context.Database.GenerateCreateScript(); + + var reader = LinesReader.FromText(sql); + + Assert.Equal(ExpectedDdlScriptForBlog(), reader.NextLines(4)); + + reader.Skip(3); + Assert.Equal(ExpectedDdlScriptForPost(), reader.NextLines(6)); + + reader.Skip(3); + Assert.Equal(ExpectedDdlScriptForIndex(), reader.NextLine()); + + reader.Skip(4); + Assert.True(reader.EndOfContent()); + } + + [DocSnippet] + [DocReplace("return", "")] + private string[] ExpectedDdlScriptForBlog() + { + return + [ + "CREATE TABLE [Blog] (", + " [Id] int NOT NULL IDENTITY,", + " CONSTRAINT [PK_Blog] PRIMARY KEY ([Id])", + ");" + ]; + } + + [DocSnippet] + [DocReplace("return", "")] + [DocReplace("Item", "Post")] + private string[] ExpectedDdlScriptForPost() + { + return + [ + "CREATE TABLE [Items] (", + " [Id] int NOT NULL IDENTITY,", + " [BlogId] int NOT NULL,", + " CONSTRAINT [PK_Items] PRIMARY KEY ([Id]),", + " CONSTRAINT [FK_Items_Blog_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blog] ([Id]) ON DELETE CASCADE", + ");" + ]; + } + + [DocSnippet] + [DocReplace("return", "")] + [DocReplace("Item", "Post")] + private string ExpectedDdlScriptForIndex() + { + return "CREATE INDEX [IX_Items_BlogId] ON [Items] ([BlogId]);"; + } +} \ No newline at end of file diff --git a/EntityFramework.Explained/Schema/Relationships/UniDirectionalOneToManyWithOneDbSet.cs b/EntityFramework.Explained/Schema/Relationships/UniDirectionalOneToManyWithOneDbSet.cs deleted file mode 100644 index 858ff69..0000000 --- a/EntityFramework.Explained/Schema/Relationships/UniDirectionalOneToManyWithOneDbSet.cs +++ /dev/null @@ -1,88 +0,0 @@ -using EntityFramework.Explained._Tools.TestContexts; -using Microsoft.EntityFrameworkCore; -using QuickPulse.Explains; -using QuickPulse.Explains.Text; - -namespace EntityFramework.Explained.Schema.Relationships; - -[DocFile] -[DocContent("Because the entity used in the `DbSet` has a collection of another entity type, the latter are mapped as well.")] -public class UniDirectionalOneToManyWithOneDbSet -{ - public class Post - { - public int Id { get; set; } - public Blog Blog { get; set; } = default!; - } - - public class Blog - { - public int Id { get; set; } - public string Name { get; set; } = default!; - } - - [Fact] - [DocHeader("Sql Server")] - [DocContent("EF infers and includes related entities in the schema even when only one side is explicitly registered in the `DbContext`.")] - public void SqlServer() - { - using var context = new TestSqlServerContext(); - - var sql = context.Database.GenerateCreateScript(); - - var reader = LinesReader.FromText(sql); - Assert.Equal("CREATE TABLE [Blog] (", reader.NextLine()); - Assert.Equal(" [Id] int NOT NULL IDENTITY,", reader.NextLine()); - Assert.Equal(" [Name] nvarchar(max) NOT NULL,", reader.NextLine()); - Assert.Equal(" CONSTRAINT [PK_Blog] PRIMARY KEY ([Id])", reader.NextLine()); - Assert.Equal(");", reader.NextLine()); - Assert.Equal("GO", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("CREATE TABLE [Items] (", reader.NextLine()); - Assert.Equal(" [Id] int NOT NULL IDENTITY,", reader.NextLine()); - Assert.Equal(" [BlogId] int NOT NULL,", reader.NextLine()); - Assert.Equal(" CONSTRAINT [PK_Items] PRIMARY KEY ([Id]),", reader.NextLine()); - Assert.Equal(" CONSTRAINT [FK_Items_Blog_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blog] ([Id]) ON DELETE CASCADE", reader.NextLine()); - Assert.Equal(");", reader.NextLine()); - Assert.Equal("GO", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("CREATE INDEX [IX_Items_BlogId] ON [Items] ([BlogId]);", reader.NextLine()); - Assert.Equal("GO", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.True(reader.EndOfContent()); - } - - [Fact] - [DocHeader("Sqlite")] - [DocContent("Same behavior as SqlServer, relationship discovery pulls in the `Blog` entity despite only registering `Post`")] - public void Sqlite() - { - using var context = new TestSqliteContext(); - - var sql = context.Database.GenerateCreateScript(); - - var reader = LinesReader.FromText(sql); - Assert.Equal("CREATE TABLE \"Blog\" (", reader.NextLine()); - Assert.Equal(" \"Id\" INTEGER NOT NULL CONSTRAINT \"PK_Blog\" PRIMARY KEY AUTOINCREMENT,", reader.NextLine()); - Assert.Equal(" \"Name\" TEXT NOT NULL", reader.NextLine()); - Assert.Equal(");", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("CREATE TABLE \"Items\" (", reader.NextLine()); - Assert.Equal(" \"Id\" INTEGER NOT NULL CONSTRAINT \"PK_Items\" PRIMARY KEY AUTOINCREMENT,", reader.NextLine()); - Assert.Equal(" \"BlogId\" INTEGER NOT NULL,", reader.NextLine()); - Assert.Equal(" CONSTRAINT \"FK_Items_Blog_BlogId\" FOREIGN KEY (\"BlogId\") REFERENCES \"Blog\" (\"Id\") ON DELETE CASCADE", reader.NextLine()); - Assert.Equal(");", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("CREATE INDEX \"IX_Items_BlogId\" ON \"Items\" (\"BlogId\");", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.Equal("", reader.NextLine()); - Assert.True(reader.EndOfContent()); - } -} \ No newline at end of file