EF Code First and WPF with the Chinook database. Part 1c – the unit tests

In the final part of the server side code (we have already written a Code First provider and data provider) I am going to implement some unit tests for the two methods I currently have on the data provider.

My unit tests will need to have no access to the database as that would break the encapsulation of the test so we need to mock out the raw data access layer while still providing the functionality that we would expect from the Entity Framework context.

To do this we need to make several changes to the current project to allow us to inject a mocked up context into the data provider.

The first thing we need to do is to abstract the context used by the data provider out into an interface, being as we currently have a small set of classes then the interface is simply:

public interface IContext
{
    IDbSet<Artist> Artist { get; set; }

    IDbSet<Album> Album { get; set; }

    IDbSet<Track> Track { get; set; }
}

Notice the IDbSet<T> – this is to allow us to mock up the DbSet’s using the same interface.

Next up we need to edit our Chinook class so that it uses this new interface (note, again, the IDbSet<T>’s)

public class Chinook : DbContext, IContext
{
    protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }

    public IDbSet<Artist> Artist { get; set; }

    public IDbSet<Album> Album { get; set; }

    public IDbSet<Track> Track { get; set; }
}

Now we have this we can use the interface in the data provider and set it to a Chinook instance by default (for the time being).

private IContext db = new Chinook();

Right we now have a good basis to start working on the test so add a new test project to the solution and create a new class called MockDbSet.  This allows us to simulate the actions of a real DbSet in the database for a given set of objects.  Huge thanks to Julie Lerman for showing the starting point for this work with her series on EF4 repositories (A great read!).

The MockDbSet implements the IDbSet<T> Interface supplied by the Entity Framework and allows us to replicate the functionality as follows (note I have not implemented the create or find methods as I don’t need them right now.)

public class MockDbSet<T> : IDbSet<T> where T : class
{
    readonly IList<T> _container = new List<T>();

    public T Add(T entity)
    {
        _container.Add(entity);
        return entity;
    }

    public T Attach(T entity)
    {
        _container.Add(entity);
        return entity;
    }

    public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
    {
        throw new NotImplementedException();
    }

    public T Create()
    {
        throw new NotImplementedException();
    }

    public T Find(params object[] keyValues)
    {
        throw new NotImplementedException();
    }

    public System.Collections.ObjectModel.ObservableCollection<T> Local
    {
        get
        {
            return new ObservableCollection<T>(_container);
        }
    }

    public T Remove(T entity)
    {
        _container.Remove(entity);
        return entity;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _container.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _container.GetEnumerator();
    }

    public Type ElementType
    {
        get
        {
            return typeof(T);
        }
    }

    public System.Linq.Expressions.Expression Expression
    {
        get
        {
            return _container.AsQueryable<T>().Expression;
        }
    }

    public IQueryProvider Provider
    {
        get
        {
            return _container.AsQueryable<T>().Provider;
        }
    }
 }

This is a simple implementation that stores the set in a local List.

Now we have a mock set we can mock up the context which is something that implements IContext and allows us to access the DbSets we want to work with outside of a database environment.  I have added some methods to add test data into the sets as well.

class MockContext : IContext
{
    private IDbSet<Artist> artist;
    private IDbSet<Album> album;
    private IDbSet<Track> track;

    public IDbSet<Artist> Artist
    {
        get
        {
            this.CreateArtists();
            return this.artist;
        }

        set
        {
            throw new NotImplementedException();
        }
    }

    public IDbSet<Album> Album
    {
        get
        {
            this.CreateAlbums();
            return this.album;
        }
        
        set
        {
            throw new NotImplementedException();
        }
    }

    public IDbSet<Track> Track
    {
        get
        {
            this.CreateTracks();
            return this.track;
        }
        
        set
        {
            throw new NotImplementedException();
        }
    }

    private void CreateArtists()
    {
        if (artist == null)
        {
            artist = new MockDbSet<Artist>();
            artist.Add(new Artist { ArtistId = 1, Name = "Test Artist 1" });
            artist.Add(new Artist { ArtistId = 2, Name = "Test Artist 2" });
            artist.Add(new Artist { ArtistId = 3, Name = "Test Artist 3" });
        }
    }

    private void CreateAlbums()
    {
        if (album == null)
        {
            album = new MockDbSet<Album>();
            album.Add(new Album { AlbumId = 1, Title = "Test Album 1", ArtistId = 1 });
            album.Add(new Album { AlbumId = 2, Title = "Test Album 2", ArtistId = 2 });
            album.Add(new Album { AlbumId = 3, Title = "Test Album 3", ArtistId = 3 });
            album.Add(new Album { AlbumId = 4, Title = "Test Album 4", ArtistId = 3 });
        }
    }

    private void CreateTracks()
    {
        if (track == null)
        {
            track = new MockDbSet<Track>();
            track.Add(new Track { TrackId = 1, Name = "Test Track 1", AlbumId = 1 });
            track.Add(new Track { TrackId = 2, Name = "Test Track 2", AlbumId = 1 });
            track.Add(new Track { TrackId = 3, Name = "Test Track 3", AlbumId = 1 });
            track.Add(new Track { TrackId = 4, Name = "Test Track 4", AlbumId = 2 });
            track.Add(new Track { TrackId = 5, Name = "Test Track 5", AlbumId = 2 });
            track.Add(new Track { TrackId = 6, Name = "Test Track 6", AlbumId = 2 });
            track.Add(new Track { TrackId = 7, Name = "Test Track 7", AlbumId = 3 });
            track.Add(new Track { TrackId = 8, Name = "Test Track 8", AlbumId = 3 });
            track.Add(new Track { TrackId = 9, Name = "Test Track 9", AlbumId = 4 });
            track.Add(new Track { TrackId = 10, Name = "Test Track 10", AlbumId = 4 });
        }
    }
}

Now we have a context and sets mocked up we can almost write our tests but at the moment we have no way of getting the new mock context into the data provider.  This is where we can use an often overlooked assembly attribute, InternalsVisibleTo, which allows the given project access to internal members of the a project.  So all we need to do is apply the following to the AssemblyInfo.cs class of the model project.

[assembly: InternalsVisibleTo("MusicApp.Model.Tests")]

and change the context member in the data provider (db) from private to internal and we can access it from the our test project.

I have two tests – one for each method in the data provider and have implemented them as follows:

public class EFCodeFirstDataProviderTest
{
    [TestMethod()]
    public void GetArtistsTest()
    {
        EFCodeFirstDataProvider target = new EFCodeFirstDataProvider() { db = new MockContext() };

        List<Artist> expected = new List<Artist>();
        expected.Add(new Artist { ArtistId = 1, Name = "Test Artist 1" });
        expected.Add(new Artist { ArtistId = 2, Name = "Test Artist 2" });
        expected.Add(new Artist { ArtistId = 3, Name = "Test Artist 3" });

        List<Artist> actual = target.GetArtists();
        CollectionAssert.AreEquivalent(expected, actual);
    }

    [TestMethod()]
    public void SearchArtistsTest()
    {
        EFCodeFirstDataProvider target = new EFCodeFirstDataProvider() { db = new MockContext() };

        string searchTerm = "Artist 1";
        List<Artist> expected = new List<Artist>();
        expected.Add(new Artist { ArtistId = 1, Name = "Test Artist 1" });

        List<Artist> actual = target.SearchArtists(searchTerm);

        CollectionAssert.AreEquivalent(expected, actual);
    }
}

As you can see they are simple tests to determine that the methods return the objects that I wanted.  As they stand these methods will fail as the CollectionAssert.AreEquivalent method checks for equality and we have no override for equality (or hash code) in our Artist class so we just need to add them in:

public override int GetHashCode()
{
    return ArtistId;
}

public override bool Equals(object obj)
{
    return ArtistId == (obj as Artist).ArtistId;
}

and the tests will now pass.

In the next post I will create a simple WPF front-end to display the information and then I will build the interface from that point on.

Advertisements

EF Code First and WPF with the Chinook database. Part 1b – the provider

I like to isolate my data access code to a single point so that my client does not have to worry about creating contexts or anything of the sort.  This makes it simpler to change the way the data is delivered to the client as it is abstracted away.

In thinking about what methods I would need for this I discovered that for the first phase I would only need two:

  • GetArtists() – Returns all artists.
  • SearchArtists(string searchTerm) – Searches for artists and returns any matches.

I don’t need any methods at the moment to fetch albums or tracks because I have virtual lazy loading properties set up to deal with them.  I will be adding more methods to this provider as the series grows (such as adding, deleting…etc).

Lets go ahead and test those lazy properties:

class Program
{
    static Chinook db = new Chinook();

    static void Main(string[] args)
    {
        // Using ToList() executes the command so the datareader is closed.
        var artists = db.Artist.ToList();
        foreach (var a in artists)
        {
            Console.WriteLine(a.Name);
            PrintAlbums(a.Albums);
        }

        Console.ReadLine();
    }

    static void PrintAlbums(ICollection<Album> albums)
    {
        foreach (var a in albums)
        {
            Console.WriteLine("\t" + a.Title);
            PrintTracks(a.Tracks);
        }
    }

    static void PrintTracks(ICollection<Track> tracks)
    {
        foreach (var t in tracks)
        {
            Console.WriteLine("\t\t" + t.Name);
        }
    }
}

image

Whoops – seems we have an invalid column name “ArtistArtistId”.

This is due to the way Code First decides on what the foreign key columns are called if we don’t explicitly include them – the default is “{TypeName}{TypeName}Id” which we obviously don’t have in the database.

We need to add the foreign key properties to our objects so EF picks up the correct names (this needs to be done for both Album and Track – note the new ArtistId and AlbumId respectively):

public class Album
{
    public int AlbumId { get; set; }

    public string Title { get; set; }

    public int ArtistId { get; set; }

    public virtual Artist Artist { get; set; }

    public virtual ICollection<Track> Tracks { get; set; }
}

public class Track
{
    public int TrackId { get; set; }

    public string Name { get; set; }

    public int AlbumId { get; set; }

    public virtual Album Album { get; set; }

    public string Composer { get; set; }
}

We can now run our demo and get a correct output:

image

We are now in a position to implement our simple data provider.

I am going to put the data provider behind an interface to allow for easy extension and dependency injection should we want to go that way.

public interface IDataProvider
{
    List<Artist> GetArtists();

    List<Artist> SearchArtists(string searchTerm);
}

This is then implemented as follows for the code first model:

public class EFCodeFirstDataProvider : IDataProvider
{
    private Chinook db = new Chinook();

    public List<Artist> GetArtists()
    {
        return this.db.Artist.ToList();
    }

    public List<Artist> SearchArtists(string searchTerm)
    {
        return this.db.Artist.Where(a => a.Name.Contains(searchTerm)).ToList();
    }
}

We have a private instance of the Chinook data context that controls all of the fetching of data.  When the Artist requests its albums then this context is used.  This is the same with the tracks.

The intention here is to allow the client to call simple methods to interact with the data and we are able to swap out for another provider if we so wish easily.

The only thing we now need to change is the little test app so that it uses the new data provider (we will hard code the type here but will be using dependency injection in our WPF app).

static IDataProvider dp = new EFCodeFirstDataProvider();

static void Main(string[] args)
{
    var artists = dp.GetArtists();
    foreach (var a in artists)
    {
        Console.WriteLine(a.Name);
        PrintAlbums(a.Albums);
    }

    Console.ReadLine();
}

And that’s all there is to it – we now have a separate data provider to work with our entity framework objects.

The next part of this series (1c) is going to involve writing unit tests for the data provider.  This is easier said than done and involves much mocking of the EF classes.

EF Code First and WPF with the Chinook database. Part 1 – the model

Introduction

I am going to start a series of posts building a simple app using the Chinook database as a back-end and putting a WPF client on the front-end.  I am going to keep it loose as I don’t want to commit to blog posts that I may not get chance to complete so I will keep every post self-contained covering 1 detail of the implementation.

The technologies and tools I plan on using for this series are as follows:

As you can see the idea is to use the latest technologies available to build a simple app to display the music details from the Chinook database.  As I am not committing to a large series I will say that my intentions for the series include:

  • Setting up the model using EF4 Code First (this post).
  • creating a basic screen listing the artists and associated albums.
  • Creating an album details adorner to show the tracks of individual albums.

Possible future posts will include:

  • Adding the ability for customers to place an order (creating invoices).
  • Adding employees to manage the customer invoices (this is a WPF app so the employee will be doing all of the work as this is not a customer facing app).
  • Adding the ability to create custom playlists that can reviewed.

Note that all of these items already exist in the Chinook database and I will be focusing on the data access and UI layers.

Building the model using EF Code First

The first thing we need to do is create a new solution for our app.  Being as we are creating the model first I am going to create a class library.

Once the solution and project are in place we need to get the Code First bits into the project.  I am going to use NuGet so you need to have this installed.

In the NuGet package manager window we can find the required package by firing the command:

Get-Package –remote –filter CodeFirst

This will give us the following output:

image

This shows us the package that we need to install is EFCodeFirst so we issue

Install-Package EFCodeFirst

this should add the EntityFramework reference to your project but it didn’t do that on my class library so I had to add the reference manually ({SolutionFolder}\packages\EFCodeFirst.0.8\lib\EntityFramework.dll).

Now we have the reference we can start to create our POCO objects.  I am going to create a model that initially uses the Artist, Album and Track tables as they are going to be used in the first part of this app – I will add more as I go along.

EF Code First allows us to create standard POCOs to represent our model so for my initial model I have:

public class Artist
{
    public int ArtistId { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Album> Albums { get; set; }
}

public class Album
{
    public int AlbumId { get; set; }
    public string Title { get; set; }
    public virtual Artist Artist { get; set; }
    public virtual ICollection<Track> Tracks { get; set; }
}

public class Track
{
    public int TrackId { get; set; }
    public string Name { get; set; }
    public virtual Album Album { get; set; }
    public string Composer { get; set; }
}

Note the virtual association properties – this is to allow these properties to be lazy-loaded.

The next thing we need is the actual dbContext which is defined as follows:

using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions.Edm.Db;
.
.
.
public class Chinook : DbContext
{
    protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }

    public DbSet<Artist> Artist { get; set; }
    public DbSet<Album> Album { get; set; }
    public DbSet<Track> Track { get; set; }
}

This is a class that includes one property per table in the database and handles the mapping to our objects.  This is using default conventions with the exception of the one removed in the overridden OnModelCreating method.  This says that the database we have doesn’t have pluralised names as Code First expects and instead the names match the entities.

Next we need to add an app.config file to the project and add the ConnectionString for the database.  I use SQLServer so adjust accordingly.

<connectionStrings>
    <add name="Chinook" providerName="System.Data.SqlClient"
        connectionString="Data Source=.;Initial Catalog=chinook;Integrated Security=true" />
</connectionStrings>

Notice that the name of the connectionString must be the same as the class name when using the default conventions.

Testing that it works

OK, we have the model built now and can run a crude test to see if it works.  Add a console app to your solution and set it as the startup project.  Add references to the Model assembly and the Code First assembly and move the app.config to the new project. Now add the following code to the main method.

static void Main(string[] args)
{
    using (Chinook db = new Chinook())
    {
        foreach (var artist in db.Artist)
        {
            Console.WriteLine(artist.Name);
        }

        Console.ReadLine();
    }
}

This will loop through all of the artists and print their name to the screen.

That’s all there is to creating a simple model with Entity Framework Code First CTP5.  In the next instalment I plan on writing a simple data provider class that will abstract the actual DbContext calls away from the user and allow the user to simply call methods like provider.GetArtists().