[WebApi Tutorial] Library Check – Services and DbContexts

Today’s header image was created by Clem Onojeghuo, the original source for the image is available here
Over the next few weeks, I’m going to be building a WebApi project and I hope you’ll join me. The WebApi project that we’ll build together will be a microservice (or sorts) that we can send an HTTP GET request to and it will respond the details of a book from the Discworld canon.
Those who know me will know that I’m a big Terry Pratchett fan, so it seemed like a good idea to base at least one tutorial around the Discworld books.
Our WebApi is going to expose a few GET enpoints, one which takes a book id and one which takes a search string. The Id endpoint will return the book and the search endpoint will return a list of 0 or more books. Each book that is returned will include a truncated list of main characters who make appearances in that book.
We’ll use Entity Framework Core as a ORM and SQLite as the database technology.
I decided on SQLite because I’m doing this cross platform, and thought ‘why not use something new for the database?’
The database records will represent Books and Characters, with a Many to Many relationship between them (meaning that we’ll need a join table).
Before We Begin
As a head’s up, I’ve already uploaded all of the code that we’re about to write to a GitHub repository. You can head over there if you want to take a sneak peak at what we’re going to create in this tutorial.
Data Models
In order to map our problem domain, we’ll need three models:
- Book
- Character
- BookCharacter
We could have named the final model CharacterBook, or JoinTable, or anything really. But BookCharacter seems like it makes the most sense to me – after all, it links a Book to a Character.
Book
The primary key will be an integer,
We could use something else, but Primary Keys are (usually) easier to handle when they’re integers – especially for smaller data sets.
we’ll need to store an ordinal (the release order, because we can’t guarantee that Entity Framework Core will persist them to the database in sequential order), the name of the book, ISBN details (both ISBN-10 and ISBN-13), and a short synopsis. Each of these properties (apart from the primary key and ordinal) will be strings.
Character
This will be the easier of our models to create: we need a primary key (integer) and a character name.
BookCharacter
This will be our join table, and will have two foreign keys:
- One for the Book Id
- One for the Character Id
Relationship Diagram
Although the domain model isn’t that complex, here’s a relationship diagram showing how our three data models fit together:

I’ve included Audit Data in the image, we’ll come back to this in a later post, but we’ll achieve this using Shadow Properties.
Which I’ve written about previously.
One last thing about the books is that the models are designed such that we can swap the data out for any other books. We could just as easily have them represent list a set of development books (swapping characters for authors), or perhaps a list of horror books. The point is that I’ve only chosen Discworld here because I’m familiar with them, but once we’ve gotten to the point of seeding our database (as it will be readonly), we can change the seed data for anything we want.
Prerequisites
There’s a few things you’ll need to know and have installed in order to follow this tutorial.
Software
The most important thing will be that you have the latest version of the .NET Core SDK installed on your development machine. Head over here, to get the latest version for your operating system: https://www.microsoft.com/net/core
On top of that you’ll either need Visual Studio Code and the C# language bindings, or Visual Studio.
If you’re not running Windows, then you won’t be able to use Visual Studio, so you’ll have to get Visual Studio Code and the C# bindings.
If you’re going down the Visual Studio Code route, then I’ve already written about the steps you’ll need to follow.
If you’re going down the Visual Studio route, then you’ll need to visit https://www.visualstudio.com/ to get the latest version. There may be a cost involved, depending on which version you want to install.
Community Edition is free, and fully featured enough to complete this tutorial – so I’d install that version if you don’t have want to buy a license right now.
Creating the WebApi Application
I’ve only briefly covered creating a WebApi application in the past, so this will be a chance for me to cover the process of creating them in greater detail.
If you want to use Visual Studio Code and the Terminal (which you’ll have to do if you’re running MacOS or one of the officially supported Linux distributions), then you should use the section called ‘Visual Studio Code and the Terminal – MacOS, Linux or Windows’.
However, if you are running Windows and would like to use Visual Studio, then please use the section called ‘Visual Studio – Windows Only’.
Visual Studio Code and the Terminal – MacOS, Linux or Windows
So the first thing we need to do is create a directory for the webApiProject
mkdir webApiTutorial | |
cd webApiTutorial/ |
Now we need to add a global.json along with an appsettings.json into that directory
touch global.json | |
touch appsettings.json |
As a side note: we should always have a global.json file. As Brad Wilson points out:
Pro-Tip: Your .NET Core projects should *always* have a global.json file with an SDK version in them.
ALWAYS.
— Brad Wilson (@bradwilson) 21 Nov 2016
This is due to how the SDK versions differ:

Once you’ve created these files, open the top level directory (I named it webApiTutorial in the previous steps) with Visual Studio Code.

In the above image, I’ve already created the src directory but we don’t want to do that yet. You’ll see why in a moment
In our global.json, we’ll need to paste the following code:
{ | |
"projects": [ | |
"src" | |
], | |
"sdk": { | |
"version": "1.0.0-preview2-final" | |
} | |
} |
This tells .NET Core that our project code will be located in the src directory (which we’ll create in a moment), and that we want to target .NET Core version 1.0.0-preview2-final (which means that we can’t use MSBuild).
We’re going with preview-2 here because preview-3 (at the time of writing) is only available for Visual Studio 2017, which some people might not want to install as it hasn’t been full released yet.
In the appsettings.json, we need to paste the following code:
{ | |
"Logging": { | |
"IncludeScopes": false, | |
"LogLevel": { | |
"Default": "Trace", | |
"System": "Information", | |
"Microsoft": "Information" | |
} | |
}, | |
"Data": { | |
"SqliteConnection":{ | |
"ConnectionString":"Data Source=dwDatabase.db" | |
} | |
} | |
} |
This gives .NET Core a heads-up on what we’ll be doing. First, we’re telling .NET Core that we want standard trace logging (which will be outputted to the terminal window once we start sending requests), then we’re telling .NET Core what our connection string is going to be.
Heading back to the terminal, run the following command (assuming that it’s still in the webApiTutorial directory):
yo aspnet |
Choose “WebApi Application” and give it the name “src”.
src is a bit of a silly name, but we’re bootstrapping using yeoman and the files that it gives us (which makes this step quicker). By running yeoman, we’re short circuiting a lot of the project setup.

Once yeoman has finished, go back to VS Code and open the following files from within the src directory:
- Program.cs
- Startup.cs
Change the namespaces in these files to ‘webApiTutorial’. Then open the ValuesController.cs file, and change the namespace to ‘webApiTutorial.Controllers’
We’ll need it to test that everything went well. But likely delete it, in one of the later posts in this series.
We also need to change the default name of the compiled binary, by default it will be the name of the source directory.
And having a binary called ‘src’ is a little naff.
Add the following to the top of your project.json:
"name": "webApiTutorial" |
The final step here is to check that everything went well so far. So let’s run the application:
dotnet restore | |
dotnet watch run |
‘dotnet watch’ will tell Roslyn to watch for file alterations and re-build and re-run the application whenever a file is saved.
Then make a note of the url listed in the output of dotnet run
By default, it comes out as ‘http://localhost:5000’ for me.
and head over to the url + /api/values.

If everything went right so far, then you should get a response exactly like the above one. If not, then I’d recommend going back and reading over everything so far.
Visual Studio – Windows Only
It is a lot easier to use Visual Studio to create the project, as we don’t have to use yeoman to create a bunch of files then futz with them.
In Visual Studio go to File > New Project:

In the dialogue window that opens, select .NET Core from the left hand portion of the window (this is the templates list), then select ‘ASP.NET Core Web Application (.NET Core)’ from the main portion of the dialogue.
Don’t forget to set the name of the project and solution to ‘webApiTutorial’

Then click ‘OK’ and in the final dialogue select ‘Web API’ (leaving all other options as they are)

You should end up with a project directory structure similar to this (in the Solution Explorer):
I added the appsettings.json file to the solutions directory before taking this screen shot, so you’ll need to do that:
- Right click on the ‘Solution Items’ directory
- Hover over Add, then click on New Item
There may not be an option for json files in the dialogue that appears, this is a common issue with Visual Studio. Leave the selections as they are and enter ‘appsettings.json’ into the file name.

The last thing we need to do is add the contents for our global.json and appsettings.json files. As with the section on using VS Code and the Terminal (above), we’re just going to paste the contents of the files in.
In our global.json, we’ll need to paste the following code:
{ | |
"projects": [ | |
"src" | |
], | |
"sdk": { | |
"version": "1.0.0-preview2-final" | |
} | |
} |
This tells .NET Core that our project code will be located in the src directory, and that we want to target .NET Core version 1.0.0-preview2-final (which means that we can’t use MSBuild).
A configuration error can happen here, if you haven’t got the correct version of the SDK installed. If that happens, see the Prerequisites section for details
In the appsettings.json, we need to paste the following code:
{ | |
"Logging": { | |
"IncludeScopes": false, | |
"LogLevel": { | |
"Default": "Trace", | |
"System": "Information", | |
"Microsoft": "Information" | |
} | |
}, | |
"Data": { | |
"SqliteConnection":{ | |
"ConnectionString":"Data Source=dwDatabase.db" | |
} | |
} | |
} |
This gives .NET Core a heads-up on what we’ll be doing. first, we’re telling .NET Core that we want standard trace logging, then we’re telling .NET Core what our connection string is going to be.
Adding Entity Framework Core
We need to add some things to our project.json to enable us to use Entity Framework Core. Firstly we need to add Entity Framework Core (and it’s SQLite Data Provider) to the dependencies section:
"dependencies": { | |
"Microsoft.NETCore.App": { | |
"version": "1.1.0", | |
"type": "platform" | |
}, | |
"Microsoft.AspNetCore.Mvc": "1.1.0", | |
"Microsoft.AspNetCore.Routing": "1.1.0", | |
"Microsoft.AspNetCore.Server.IISIntegration": "1.1.0", | |
"Microsoft.AspNetCore.Server.Kestrel": "1.1.0", | |
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.1.0", | |
"Microsoft.Extensions.Configuration.FileExtensions": "1.1.0", | |
"Microsoft.Extensions.Configuration.Json": "1.1.0", | |
"Microsoft.Extensions.Configuration.CommandLine": "1.1.0", | |
"Microsoft.Extensions.Logging": "1.1.0", | |
"Microsoft.Extensions.Logging.Console": "1.1.0", | |
"Microsoft.Extensions.Logging.Debug": "1.1.0", | |
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0", | |
"Microsoft.EntityFrameworkCore.Design": "1.1.0", | |
"Microsoft.EntityFrameworkCore": "1.1.0", | |
"Microsoft.EntityFrameworkCore.Sqlite": "1.1.0", | |
"Microsoft.EntityFrameworkCore.Sqlite.Design": { | |
"version": "1.1.0", | |
"type": "build" | |
}, | |
"Microsoft.EntityFrameworkCore.Tools": { | |
"version": "1.0.0-preview2-final", | |
"type": "build" | |
} | |
}, |
Here, I’ve added the entirety of the dependencies section of the project.json, and I’ve highlighted the lines that you should add.
We should also add the Entity Framework Core Tools to the tools section:
"tools": { | |
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.1.0-preview4-final", | |
"Microsoft.EntityFrameworkCore.Tools.DotNet": "1.0.0-preview3-final" | |
}, |
And the last thing you need to do is to change the default namespace prefix. This will help us when we create new classes, as it will set a useful default namespace:
"tooling": { | |
"defaultNamespace": "webApiTutorial" | |
}, |
If you used Visual Studio to create the project, this might already be set.
Now that we’ve added our dependencies, we need to restore them after stopping the application (if it’s still running from earlier)
dotnet restore |
Visual Studio should pull the dependencies as soon as you save the project.json, and VS Code might prompt you to. So you might not need to run this command.
Creating and Adding The Book Model
Before we add the Data Context, we need to add our first Data Model class. This is because the Database Context will need to create a DbSet of Book models.
Create a sub-directory (within the src directory) called Models, create a file called Book.cs within it and paste the following code into it:
namespace webApiTutorial.Models | |
{ | |
public class Book | |
{ | |
public int BookId { get; set; } | |
public int BookOrdinal { get; set; } | |
public string BookName { get; set; } | |
public string BookIsbn10 { get; set; } | |
public string BookIsbn13 { get; set; } | |
public string BookDescription { get; set; } | |
} | |
} |
That’s about it, really. Pretty simple, eh?
Adding a Database Context
So the first thing we need to do is tell .NET Core what our connection string is. To do that, we’ll need to paste the following code into the appsettings.json file within the src directory:
{ | |
"Logging": { | |
"IncludeScopes": false, | |
"LogLevel": { | |
"Default": "Debug", | |
"System": "Information", | |
"Microsoft": "Information" | |
} | |
}, | |
"Data": { | |
"SqliteConnection":{ | |
"ConnectionString":"Data Source=dwDatabase.db" | |
} | |
} | |
} |
Now that we have the connection string, we should go create our DbContext class. Create a directory called ‘DatabaseContexts’ to the src directory, add a file called ‘dwContext.cs’ to that sub-directory, then paste the following code into it:
using Microsoft.EntityFrameworkCore; | |
using webApiTutorial.Models; | |
namespace webApiTutorial.DatabaseContexts | |
{ | |
public class DwContext : DbContext | |
{ | |
public DwContext(DbContextOptions<DwContext> options) : base(options) { } | |
public DwContext() { } | |
protected override void OnModelCreating(ModelBuilder modelBuilder) | |
{ | |
} | |
public override int SaveChanges() | |
{ | |
return base.SaveChanges(); | |
} | |
public DbSet<Book> Books { get; set; } | |
} | |
} |
The DwContext class will be what we use to communicate with the database. We wont interact with with the context directly from our controllers (we’ll use services for that).
If we need to perform any specific commands when the DbModel is created (for instance, to add Shadow Properties for auditing), we’ll do that in the OnModelCreating method. We’ll also use the SaveChanges method (at a later stage) to apply audit information and persist our seed data to the database.
But we don’t need to worry about those just yet.
Adding a Book Service
As I mentioned in the previous section, our controllers will not interact with the DbContext directly, they’ll use a separate service for each of our models.
We’ll inject our services where they’re needed via .NET Core’s built in support for dependency injection, so we’ll need to start with an Interface.
Create a sub-directory (within the ‘src’ directory) called ‘Services’, add a file called ‘IBookService.cs’ to that sub-directory, and paste the following code into it:
using System.Collections.Generic; | |
using webApiTutorial.Models; | |
namespace webApiTutorial.Services | |
{ | |
public interface IBookService | |
{ | |
// Search and Get | |
Book FindByOrdinal (int id); | |
IEnumerable<Book> Search(string searchKey); | |
} | |
} |
A quick side note about Interfaces (for those who don’t know), is that they define the public methods that a class which implements it MUST implement.
Now create a file called ‘BookService.cs’ (again, in the Services directory) and paste the following into it:
using Microsoft.EntityFrameworkCore; | |
using System.Collections.Generic; | |
using System.Linq; | |
using webApiTutorial.DatabaseContexts; | |
using webApiTutorial.Models; | |
namespace webApiTutorial.Services | |
{ | |
public class BookService : IBookService | |
{ | |
private DwContext _dwContext; | |
public BookService (DwContext dwContext) | |
{ | |
_dwContext = dwContext; | |
} | |
public Book FindByOrdinal (int id) | |
{ | |
return BaseQuery() | |
.FirstOrDefault(book => book.BookOrdinal == id); | |
} | |
public IEnumerable<Book> Search(string searchKey) | |
{ | |
var blankSearchString = string.IsNullOrWhiteSpace(searchKey); | |
var results = BaseQuery(); | |
if (!blankSearchString) | |
{ | |
searchKey = searchKey.ToLower(); | |
results = results | |
.Where(book => book.BookName.ToLower().Contains(searchKey) | |
|| book.BookDescription.ToLower().Contains(searchKey) | |
|| book.BookIsbn10.ToLower().Contains(searchKey) | |
|| book.BookIsbn13.ToLower().Contains(searchKey)); | |
} | |
return results.OrderBy(book => book.BookOrdinal); | |
} | |
private IEnumerable<Book> BaseQuery() | |
{ | |
return _dwContext.Books.AsNoTracking(); | |
} | |
} | |
} |
There’s an awful lot happening here, so let’s take a deeper look at the file in chunks:
private DwContext _dwContext; | |
public BookService (DwContext dwContext) | |
{ | |
_dwContext = dwContext; | |
} |
Here we’re making use of .NET Core’s dependency injection (we’ll configure it in a moment), to supply us with an instance of the DwContext object (which contains our DbSets).
private IEnumerable<Book> BaseQuery() | |
{ | |
return _dwContext.Books.AsNoTracking(); | |
} |
Here we’re creating a base (read-only) query for all of our other methods to use. This is useful because we don’t have to repeat ourselves when writing our queries.
All entities found using this base query will not have their changes tracked.
We’ll come to the Entity Framework Change Tracker in a later part of this tutorial series
So if we want to update any database entities, we’ll have to use a different query to first get the data, alter it in some way, then save the changes to them.
public Book FindByOrdinal (int id) | |
{ | |
return BaseQuery() | |
.FirstOrDefault(book => book.BookOrdinal == id); | |
} | |
public IEnumerable<Book> Search(string searchKey) | |
{ | |
var blankSearchString = string.IsNullOrWhiteSpace(searchKey); | |
var results = BaseQuery(); | |
if (!blankSearchString) | |
{ | |
searchKey = searchKey.ToLower(); | |
results = results | |
.Where(book => book.BookName.ToLower().Contains(searchKey) | |
|| book.BookDescription.ToLower().Contains(searchKey) | |
|| book.BookIsbn10.ToLower().Contains(searchKey) | |
|| book.BookIsbn13.ToLower().Contains(searchKey)); | |
} |
Here we’re creating our two service methods for finding a book entity by an ordinal and for searching through the book records.
Adding Services and DbContexts via Dependency Injection
The final thing we need to do, in order to use our Service (and by extension the DbContext) in any controllers that we create is to alter the Startup.cs class.
The Startup.cs class contains all of the code required to get Kestrel (.NET Core’s web server) up and running, it’s called from within the Program.cs file at line 22:
.UseContentRoot(Directory.GetCurrentDirectory()) | |
.UseIISIntegration() | |
.UseStartup<Startup>() | |
.Build(); | |
Open the Startup.cs class and paste the following changes into it:
using Microsoft.AspNetCore.Builder; | |
using Microsoft.AspNetCore.Hosting; | |
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Logging; | |
using Microsoft.EntityFrameworkCore; | |
using webApiTutorial.DatabaseContexts; | |
using webApiTutorial.Services; | |
Before we can reference our types, we need to reference them. This isn’t too exciting, but the next thing might be:
public void ConfigureServices(IServiceCollection services) | |
{ | |
// Add framework services. | |
services.AddMvc(); | |
// Give ourselves access to the DwContext | |
services.AddDbContext<DwContext>(options => | |
options.UseSqlite(Configuration["Data:SqliteConnection:ConnectionString"])); | |
// DI our Book service into our controllers | |
services.AddTransient<IBookService, BookService>(); | |
} |
The first thing we’re doing here is we’re adding a reference to our DwContext class, and telling .NET Core which key within the appsettings.json file contains our connection string.
The next thing we’re doing is mapping the interface IBookService to the concrete type BookService using AddTransient. Using AddTransient rather than any of the other methods for mapping (we’ll go into them in a moment) means that a new instance of each concrete class will be spun up for each object that requires one – this is called the object’s lifetime.
For example: if we had four controllers (actively responding requests) which used the BookService, we would end up with 4 instances of the BookService in memory.
Depending on the lifetime you want for your DI objects, you could use one of the following methods:
Transient – Transient lifetime services are created each time they are requested. This lifetime works best for lightweight, stateless services.
Scoped – Scoped lifetime services are created once per request.
Singleton – Singleton lifetime services are created the first time they are requested (or when ConfigureServices is run if you specify an instance there) and then every subsequent request will use the same instance.
Taken from the .NET Core documentation on Dependency Injection
Using Our Service – An Example
We’ll create our own controllers in the next part of this tutorial series, but we’ll use the Values controller in order to show how to use an instance of the service for now.
You don’t have to follow this part, I’m just going to show off how you might use an injected service in preparation for next time.
Open the Values Controller (found in the ‘Controllers’ sub-directory) and paste the following code blocks into it.
First, the using statement (important, but often over looked):
using System.Collections.Generic; | |
using Microsoft.AspNetCore.Mvc; | |
using webApiTutorial.Services; |
Next, we want to create a constructor, so that we can inject our dependencies into the class, and a private variable for our service instance:
public class ValuesController : Controller | |
{ | |
private IBookService _bookService; | |
public ValuesController(IBookService bookService) | |
{ | |
_bookService = bookService; | |
} |
And finally, we’re going to use the BookService that was injected into our controller:
// GET api/values | |
[HttpGet] | |
public IEnumerable<string> Get() | |
{ | |
if (_bookService != null) | |
{ | |
return new string[] { "Book service is ready" }; | |
} | |
return new string[] { "value1", "value2" }; | |
} |
If you now do a GET request for /api/values you should get the following response:
I think we’ll leave it there for now
Mainly because this article is now 3000 words long, and I can’t imagine how tedious it must be to read it by this point.
Next time, we’ll create the database (using Entity Framework Core), seed some data into the database, get a record from the database and send it back as a response.
Exciting, or what?