An index of the HomeLibrary application posts can be found here.
As I mentioned in my previous post, I ran into an interesting situation with Entity Framework 6. It went a little like this…
I kicked off my data layer with some domain objects, a HomeLibraryContext
class (deriving from DbContext
) and a HomeLibraryInitializer
class which inherits from DropCreateDatabaseAlways
. The HomeLibraryInitializer
contains a Seed
method which inserts some data into the newly created database.
I also decided that I was going to use EntityFramework Migrations
to enable me to make changes to the database as I evolve my domain model. To that end, I enabled migrations on the HomeLibrary.EF project using the Package Manager Console and created the first Migration
:
internal sealed class Configuration : DbMigrationsConfiguration<Db.HomeLibraryContext> { public Configuration() { AutomaticMigrationsEnabled = false; } protected override void Seed(Db.HomeLibraryContext context) { new List<Person> { new Person {FirstName = "Terry", LastName = "Halpin"}, new Person {FirstName = "Alan", LastName = "Turing"} }.ForEach(p => context.People.Add(p)); context.SaveChanges(); } }
The initial Migration
:
public partial class Initial : DbMigration { public override void Up() { CreateTable( "dbo.BookCovers", c => new { Id = c.Int(nullable: false, identity: true), BookId = c.Int(nullable: false), Edition = c.Int(nullable: false), Cover = c.Binary(maxLength: 4000), }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.Books", t => t.BookId, cascadeDelete: true) .Index(t => t.BookId); CreateTable( "dbo.Books", c => new { Id = c.Int(nullable: false, identity: true), Title = c.String(nullable: false, maxLength: 4000), Edition = c.Int(nullable: false), PublisherId = c.Int(nullable: false), TypeOfBook = c.Int(nullable: false), }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.Publishers", t => t.PublisherId, cascadeDelete: true) .Index(t => t.PublisherId); CreateTable( "dbo.People", c => new { Id = c.Int(nullable: false, identity: true), Email = c.String(nullable: false, maxLength: 4000), IsAuthor = c.Boolean(nullable: false), FirstName = c.String(nullable: false, maxLength: 4000), LastName = c.String(nullable: false, maxLength: 4000), Sobriquet = c.String(maxLength: 4000), }) .PrimaryKey(t => t.Id); CreateTable( "dbo.Lendings", c => new { Id = c.Int(nullable: false, identity: true), BookId = c.Int(nullable: false), BorrowerId = c.Int(nullable: false), DateLent = c.DateTime(nullable: false), DueDate = c.DateTime(), ReturnDate = c.DateTime(), }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.Books", t => t.BookId, cascadeDelete: true) .ForeignKey("dbo.People", t => t.BorrowerId, cascadeDelete: true) .Index(t => t.BookId) .Index(t => t.BorrowerId); CreateTable( "dbo.Comments", c => new { Id = c.Int(nullable: false, identity: true), BookId = c.Int(nullable: false), CommentText = c.String(nullable: false, maxLength: 4000), }) .PrimaryKey(t => t.Id) .ForeignKey("dbo.Books", t => t.BookId, cascadeDelete: true) .Index(t => t.BookId); CreateTable( "dbo.Publishers", c => new { Id = c.Int(nullable: false, identity: true), Name = c.String(nullable: false, maxLength: 4000), }) .PrimaryKey(t => t.Id); CreateTable( "dbo.PersonBooks", c => new { Person_Id = c.Int(nullable: false), Book_Id = c.Int(nullable: false), }) .PrimaryKey(t => new { t.Person_Id, t.Book_Id }) .ForeignKey("dbo.People", t => t.Person_Id, cascadeDelete: true) .ForeignKey("dbo.Books", t => t.Book_Id, cascadeDelete: true) .Index(t => t.Person_Id) .Index(t => t.Book_Id); } public override void Down() { DropForeignKey("dbo.Books", "PublisherId", "dbo.Publishers"); DropForeignKey("dbo.BookCovers", "BookId", "dbo.Books"); DropForeignKey("dbo.Comments", "BookId", "dbo.Books"); DropForeignKey("dbo.Lendings", "BorrowerId", "dbo.People"); DropForeignKey("dbo.Lendings", "BookId", "dbo.Books"); DropForeignKey("dbo.PersonBooks", "Book_Id", "dbo.Books"); DropForeignKey("dbo.PersonBooks", "Person_Id", "dbo.People"); DropIndex("dbo.Books", new[] { "PublisherId" }); DropIndex("dbo.BookCovers", new[] { "BookId" }); DropIndex("dbo.Comments", new[] { "BookId" }); DropIndex("dbo.Lendings", new[] { "BorrowerId" }); DropIndex("dbo.Lendings", new[] { "BookId" }); DropIndex("dbo.PersonBooks", new[] { "Book_Id" }); DropIndex("dbo.PersonBooks", new[] { "Person_Id" }); DropTable("dbo.PersonBooks"); DropTable("dbo.Publishers"); DropTable("dbo.Comments"); DropTable("dbo.Lendings"); DropTable("dbo.People"); DropTable("dbo.Books"); DropTable("dbo.BookCovers"); } }
I then started running into difficulties. Strange things were happening and the SQL Server Compact database was not responding to my Migrations
commands in the way I expected. So, I turned to Google.
As it turns out, there are two options for seeding the database using Code First and they are mutually exclusive:
- The original EF way of creating an
Initializer
which inherits fromDropCreateDatabaseAlways
orDropCreateDatabaseIfModelChanges
. You can see the code for this option in my last post. - Using
Migrations
, which uses the seed method in the Configuration file which inherits fromDbMigrationsConfiguration
For this project, I preferred the original EF way as I have found it much simpler to work with. I am, however, going to try and have my cake and eat it. When I made this decision, I found that all I had to do to disable Migrations
was to exclude the Migrations directory from my solution. However, if I do want to change the schema again and use Migrations
to create a Migration
, I can just include that folder again, comment out the code which sets the DatabaseInitializer
and create a new Migration
based on the current state of the domain classes. Let’s see whether this approach continues to work!