Web API in ASP.NET Core

In this post I’ll talk about the Web API in ASP.NET Core and how we can implement a Restful service with this new framework.

One of the main difference in ASP.NET Core is that Web API have an unified class with the classic MVC Controllers.

So the declaration of a Web API in ASP.NET Core is look like this:


public class PostController : Controller
{
}

The attribute of the actions verbs is pretty similar to MVC 5.

We can define the controller route as well and we can do that with the Route attribute:


[Route("api/[controller]")]
public class PostController : Controller
{
}

We can implement an action in the same way as the Web API 2:


[HttpGet("{offset}", Name = "GetPost")]
 public IActionResult Get(int offset)
 {
 var result = _postService.GetPosts(offset, true);

return Ok(result);
 }

With the HttpGet attribute we can specify the verb of the method, the parameters and the action url.

So far so good, now there is the last question about this topic, that is the content negotiation.

Since ASP.NET Core MVC and Web API frameworks are unified, with the attribute Produces we can define the response content type for the controllers.

So, if we want to return a content type in json format, we need to apply this attribute:


[Produces("application/json")]
[Route("api/[controller]")]
public class PostController : Controller
{
}

With this attribute the content type of the request will be application/json, otherwise the controller will returns a response 415 – Unsupported media type.

The source code of the topic is here.

 

Web API in ASP.NET Core

Unit Testing in .NET Core with xUnit

In this post we see how to do unit testing in .NET Core with xUnit.

xUnit is a popular framework, with a lot of features and helpers to develop quickly our unit testings and implement them with a clean syntax; it’s very efficient and the result is that the tests execution is very fast.

In the previous post we implemented a  basic class with an in-memory DbContext, that we’ll use in the unit test.

The service

We can suppose that our sut is a service, like CategoryService.

The service has some methods to retrieve the category from the database with Entity Framework.

The service look like this:


public class CategoryService
{
readonly BlogContextFactory _contextFactory;

public CategoryService(BlogContextFactory contextFactory)
{
_contextFactory = contextFactory;
}

public IEnumerable<Category> GetCategories()
{
using (var db = _contextFactory.GetNewDbContext())
{
return db.Categories.OrderBy(c => c.Name).ToList();
}
}

public async Task<Category> AddCategory(Category category)
{
using (var db = _contextFactory.GetNewDbContext())
{
category.Id = Guid.NewGuid();

await db.Categories.AddAsync(category);
await db.SaveChangesAsync();

return category;
}
}

public async Task UpdateCategory(Category category)
{
using (var db = _contextFactory.GetNewDbContext())
{
db.Categories.Update(category);
await db.SaveChangesAsync();
}
}

public async Task DeleteCategory(Category category)
{
using (var db = _contextFactory.GetNewDbContext())
{
db.Categories.Remove(category);
await db.SaveChangesAsync();
}
}
}

Unit Test class

Now we can implement the Unit Test class; we can use the base class DatabaseFixture and pass the context factory configured with an in-memory DbContext to the service:


public class CategoryServiceTests : DatabaseFixture
{
readonly CategoryService _categoryService;

public CategoryServiceTests() : base("CategoryContext")
{
_categoryService = new CategoryService(ContextFactory);
}

[Fact]
public void should_return_categories_list()
{
var categories = _categoryService.GetCategories();
categories.Should().NotBeNullOrEmpty();
categories.Count().Should().BeGreaterThan(0);
}

[Fact]
public async Task should_add_new_category()
{
var category = new Category()
{
Id = new Guid("{1225FE5B-9C46-4BA3-9233-9337DDC5F478}"),
Name = "Test Category"
};

category = await _categoryService.AddCategory(category);

using (var db = ContextFactory.GetNewDbContext())
{
var newCategory = await db.FindAsync<Category>(category.Id);
newCategory.Should().NotBeNull();
}
}

[Fact]
public async Task should_edit_category()
{
using (var db = ContextFactory.GetNewDbContext())
{
var category = await db.FindAsync<Category>(new Guid("{BE7EB1E1-FB67-4FE1-A96E-4721D37AFE29}"));
category.Name = "category modified";
await _categoryService.UpdateCategory(category);

category = await db.FindAsync<Category>(new Guid("{BE7EB1E1-FB67-4FE1-A96E-4721D37AFE29}"));
category.Name.ShouldBeEquivalentTo("category modified");
}
}

[Fact]
public async Task should_delete_category()
{
var category = new Category() { Id = new Guid("{5D134CF2-EF37-4663-B3D2-64F2E3DBF3BE}"), Name = "Category1" };
await _categoryService.DeleteCategory(category);

using (var db = ContextFactory.GetNewDbContext())
{
category = await db.FindAsync<Category>(new Guid("{5D134CF2-EF37-4663-B3D2-64F2E3DBF3BE}"));
category.Should().BeNull();
}
}
}

The Fact attribute is the xUnit attribute used for the test methods.

As discussed above the ContextFactory is configured to returns an instance in-memory of the DbContext.

Now we can run the unit test by opening a command console, going to the UnitTest project folder and running the command:

dotnet test

The command will run the tests and will show us the results.

You can find the source code here.

 

Unit Testing in .NET Core with xUnit

In-memory DbContext with Entity Framework Core

Frequently, when we need to test a EF context from our unit test class, we need to implement a stub of the context manually, in order to have, for example, an in memory representation of the context.

In entity framework core this is useless because one of the options available to initialize a context is to use an in memory database.

With this option we can populate an in memory database that could be used, for test purposes.

Let’s go to take a look about the configurations that we need to do.

Context

The first thing is define a DbContext.

With Entity Framework Core, the things are quite different because the DbContext class has a new constructor that accept a parameter of type DbContextOptions; this parameter will be used to define for example the connection string of the context.

public class BlogContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Reply> Replies { get; set; }
public DbSet<PostCategory> PostCategories { get; set; }
public DbSet<PostTag> PostTags { get; set; }
public BlogContext(DbContextOptions<BlogContext> options) : base(options) { }
}

Context factory

Basically, what I want is a context factory that help me to get a new instance of a context with specific options.


public class BlogContextFactory
{
private readonly DbContextOptions<BlogContext> _options;

public BlogContextFactory(DbContextOptions<BlogContext> options)
{
_options = options;
}

public BlogContext GetNewDbContext()
{
return new BlogContext(_options);
}
}

The factory accept a DbContextOptions parameter in the constructor and returns a new instance of the BlogContext with these options.

Registration

Another important thing is the registration of the factory in a IoC container, that in my case is Autofac.

This is a mandatory stuff if I want to inject the factories in the services that depends on it:

var options = new DbContextOptionsBuilder<BlogContext>().UseSqlServer(_configuration.GetConnectionString("BlogContext")).Options;
builder.Register(b => new BlogContextFactory(options)).SingleInstance();

I’m using Autofac, so I register BlogContextFactory as single instance with specific options.

The first row is the most important; with the method UseSqlServer I’m saying that the db context will be configured with a sql server database, and obviously I’m giving the connections string as a parameter of the method.

In memory option

As well as I use this options to configure a db context with a sql database, I can do the same thing to configure an in memory database.


public abstract class DatabaseFixture : IDisposable
{
protected readonly BlogContextFactory ContextFactory;
readonly DbContextOptions<BlogContext> _options;

public DatabaseFixture(string databaseName)
{
_options = new DbContextOptionsBuilder<BlogContext>().UseInMemoryDatabase(databaseName).Options;
ContextFactory = new BlogContextFactory(_options);

var categories = new List<Category>()
{
new Category() { Id = new Guid("{5D134CF2-EF37-4663-B3D2-64F2E3DBF3BE}"), Name = "Category1" },
new Category() { Id = new Guid("{BE7EB1E1-FB67-4FE1-A96E-4721D37AFE29}"), Name = "Category2" },
new Category() { Id = new Guid("{BBB5D5DD-A746-4A87-9204-90EE87B5F560}"), Name = "Category3" }
};

var tags = new List<Tag>()
{
new Tag() { Id = new Guid("{859A4C65-F688-4F9F-8575-F389841665F8}"), Name = "Tag1" },
new Tag() { Id = new Guid("{A57D65BE-8A33-48E8-A3E4-3EF5D110A961}"), Name = "Tag2" },
new Tag() { Id = new Guid("{2D1AC63C-3D48-45A3-8F22-C9670A9D4F84}"), Name = "Tag3" }
};

var posts = new List<Post>()
{
new Post() { Id = new Guid("{7660E171-0776-451C-AD2C-1CA6F46EDB30}"), Title = "Post1", Content = "Content1", Published = true },
new Post() { Id = new Guid("{7221B957-E3F2-461F-A4F8-3D13699880A1}"), Title = "Post2", Content = "Content2", Published = false },
new Post() { Id = new Guid("{D2E934DB-7B64-42A7-953F-213EA771F596}"), Title = "Post3", Content = "Content3", Published = false }
};

var replies = new List<Reply>()
{
new Reply() { Id = new Guid("{9344DCAF-154E-4167-8EEF-0372896F5469}"), CreateUser = "user1", Content = "Content1", Published = true, Post = posts[0] },
new Reply() { Id = new Guid("{1DB75F77-A50E-4F90-83F8-711D3025EBA4}"), CreateUser = "user2", Content = "Content2", Published = true, Post = posts[0] },
new Reply() { Id = new Guid("{6772F1D7-E6D6-47DB-8724-E46353BFDFDD}"), CreateUser = "user3", Content = "Content3", Published = false, Post = posts[0] }
};

var postCategories = new List<PostCategory>()
{
new PostCategory() {
Id = new Guid("{267A0AE0-2E91-4FA5-B45C-361B8AAE635D}"),
Post = posts[0],
Category = categories[0] }
};

var postTags = new List<PostTag>()
{
new PostTag() {
Id = new Guid("{DA6CEA55-610F-4956-913F-D8F409014AC9}"),
Post = posts[0],
Tag = tags[0] }
};

using (var blogContext = ContextFactory.GetNewDbContext())
{
blogContext.Set<Category>().AddRange(categories);
blogContext.Set<Tag>().AddRange(tags);
blogContext.Set<Post>().AddRange(posts);
blogContext.Set<Reply>().AddRange(replies);
blogContext.Set<PostCategory>().AddRange(postCategories);
blogContext.Set<PostTag>().AddRange(postTags);
blogContext.SaveChanges();
}
}

public void Dispose()
{
using (var blogContext = ContextFactory.GetNewDbContext())
{
blogContext.RemoveRange(blogContext.Categories);
blogContext.RemoveRange(blogContext.Tags);
blogContext.RemoveRange(blogContext.Posts);
blogContext.RemoveRange(blogContext.Replies);
blogContext.RemoveRange(blogContext.PostCategories);
blogContext.RemoveRange(blogContext.PostTags);
blogContext.SaveChanges();
}
}
}

With the UseInMemoryDatabase method I can configure an instance of the db context with an in memory database.

The only things that I need to do is specify the name of the database (a string) and then populate the context with the entities collections.

After that, my in memory db context will be available for the using.

The source code of this topic is here.

In-memory DbContext with Entity Framework Core