Home Library

An index of the HomeLibrary application posts can be found here.

This year I have set a little project for myself. A bit of background first. Last year I released a project called WinformsMVP which is an MVP framework for the Winforms platform. The example code that I provided with the source was quite trivial. Previous to that, in my first year as a programmer, I created a Winforms application which assisted my study for the Winforms MCTS certification. I thought it would be nice to take that application and re-implement it using the WinformsMVP framework. I will also take the opportunity to use up-to-date tools like Entity Framework 6 (code first) and perhaps a few other little utilities which I have come across in my travels.

I have already made a start on the application which I am going to call Home Library. It is basically an application which individuals can use to track their lendings of books to others. I’ve lost many books over the years where they have not been returned and I did not track who I lent it to. This application helped me address that and it was fun to make.

The code for this project is available at this GitHub repository. I plan to tag the code at various milestones and the tag for the current state of the code is called DataAccessAndDomain_1. Mind you, that doesn’t mean I won’t go back and re-factor code. But I think tagging it at various milestones will be helpful as I make blog posts with regards to the project, as it progresses.

I don’t have much in the way of Business Analysis skills; and in any case, I am the client and subject-matter expert here. So I got to work and created the domain classes which I used to generate my database. The database schema looks like this (diagram generated using the Entity Framework Power Tools): Home Library Schema

To give you an idea of the domain classes, here are a couple which I have created:

public class Book
{
	public int Id { get; set; }
	public string Title { get; set; }
	public Edition Edition { get; set; }
	public Publisher Publisher { get; set; }
	public int PublisherId { get; set; }
	public BookType TypeOfBook { get; set; }

	public virtual ICollection<Person> Authors { get; set; }
	public virtual ICollection<Comment> Comments { get; set; }
	public virtual ICollection<BookCover> Covers { get; set; }
	public virtual ICollection<Lending> Lendings { get; set; }
}
public class Person
{
	public int Id { get; set; }
	public string Email { get; set; }
	public bool IsAuthor { get; set; }
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public string Sobriquet { get; set; }

	public virtual ICollection<Lending> Lendings { get; set; }
	public virtual ICollection<Book> Books { get; set; }
}

Those classes are in a separate project called HomeLibrary.Model.
You can see in the book class I have created a couple of enums. As Entity Framework 6 supports enums, it made sense to use them for abstractions which represented a finite number of options. The BookType enum looks like this:

public enum BookType
{
	TextBook = 0,
	Novel = 1
};

For the classes which will do the actual querying, I created a separate project called HomeLibrary.Model.EF. The HomeLibraryContext (which inherits from DbContext) is as follows:

public class HomeLibraryContext : DbContext
{
    //  DbSets go here
    public DbSet<Book> Books { get; set; }
    public DbSet<BookCover> BookCovers { get; set; }
    public DbSet<Comment> Comments { get; set; }
    public DbSet<Lending> Lendings { get; set; }
    public DbSet<Person> People { get; set; }
    public DbSet<Publisher> Publishers { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //  set up the Publisher's table
        modelBuilder.Entity<Publisher>().HasKey(p => p.Id);
        modelBuilder.Entity<Publisher>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<Publisher>().Property(p => p.Name).IsRequired().IsVariableLength();
            
        //  set up the People table
        modelBuilder.Entity<Person>().HasKey(p => p.Id);
        modelBuilder.Entity<Person>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<Person>().Property(p => p.Email).IsRequired().IsVariableLength();
        modelBuilder.Entity<Person>().Property(p => p.FirstName).IsRequired().IsVariableLength();
        modelBuilder.Entity<Person>().Property(p => p.LastName).IsRequired().IsVariableLength();
        modelBuilder.Entity<Person>().Property(p => p.Sobriquet).IsOptional().IsVariableLength();

        //  set up the Comment table
        modelBuilder.Entity<Comment>().HasKey(p => p.Id);
        modelBuilder.Entity<Comment>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<Comment>().Property(c => c.CommentText).IsRequired().IsVariableLength();

        //  set up the Book table
        modelBuilder.Entity<Book>().HasKey(p => p.Id);
        modelBuilder.Entity<Book>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<Book>().Property(c => c.Title).IsRequired().IsVariableLength();

        modelBuilder.Entity<BookCover>().HasKey(p => p.Id);
        modelBuilder.Entity<BookCover>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        modelBuilder.Entity<Lending>().HasKey(p => p.Id);
        modelBuilder.Entity<Lending>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);


        base.OnModelCreating(modelBuilder);
    }
}

You can see that I have opted for using the Fluent API for setting up the various tables rather than the Attribute-based API. Otherwise, it is a very straightforward context.

I faced an interesting scenario with seeding which I will leave for a separate blog post. The upshot of it was that I elected to do the database creation and seeding using the original EF Code First method of inheriting from a class which implements IDatabaseInitializer. As I wanted to drop and re-create the database each time I ran the application during development, I opted to inherit from DropCreateDatabaseAlways. The HomeLibraryInitializer looks like:

public class HomeLibraryInitializer : DropCreateDatabaseAlways<HomeLibraryContext>
{
    protected override void Seed(HomeLibraryContext context)
    {
        new List<Person> { new Person { FirstName = "Terry", LastName = "Halpin", Email = "hi", IsAuthor = false},
            new Person { FirstName = "Alan", LastName = "Turing", Email = "hi", IsAuthor = false }
            , }.ForEach(p => context.People.Add(p));

        context.SaveChanges();

        base.Seed(context);
    }
}

Stay tuned for future posts!

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>