· c-sharp api webapi entityframework di moq ef aspnet aspnetcore repository

Tutorial: ASP.NET Core Web API 2 - Controller and Repository

Tutorial: ASP.NET Core Web API, part 2

Controller & Repository

This is the second post in the tutorial about ASP.NET Core Web API. The first post we created the initial solution that this post is based upon. If you want to, you can grab the source here on Github.

Adding Endpoints

When finished with this part of the tutorial, we will be able to call the following endpoints:

HTTP Verb URL Controller Method Comment
GET api/People Get() Get all people in the database
GET api/People/1 Get(int id) Get Person with id = 1 from the database
POST api/People Post(Person model) Create a new Person in the database and returns the URI to that Person
PUT api/People/1 Put(int id, Person model) Updates an existing Person in the database
DELETE api/People/1 Delete(int id) Delete Person with id 1 in the database

All GET methods can be called from the browser, however, you need a tool like Fiddler or Postman to execute other HTTP Verbs than GET.

The Repository

Now that we have set up the solution and created our initial database in the previous post, we will move on and create the repository that our controller will be using to access the data. We could as well just implement the repository logic directly in the controller, however, then we would not be able to reuse it and testing would be harder.

We are going to add and interface to use as a contract for our repository. Again, this is for testing, primarily.

Add the below code to the PersonRepository.cs:

public interface IPersonRepository
{
    Task<Person> AddAsync(Person item);
    IEnumerable<Person> GetAll();
    Task<Person> FindAsync(int personId);
    Task RemoveAsync(int personId);
    Task UpdateAsync(Person person);
}

This interface exposes the functionality that we are going to implement in the repository.

Here is the implementation of the PersonRepository class:

public class PersonRepository : IPersonRepository
{
    private readonly ApplicationDbContext context;

    public PersonRepository(ApplicationDbContext context)
    {
        this.context = context;
    }

    public async Task<Person> AddAsync(Person item)
    {
        context.Add(item);
        await context.SaveChangesAsync();

        return item;
    }

    public IEnumerable<Person> GetAll()
    {
        return context.People.Include(p => p.Gender);
    }

    public async Task<Person> FindAsync(int id)
    {
        return await context.People.Include(p => p.Gender).FirstOrDefaultAsync(p => p.Id == id);
    }

    public async Task RemoveAsync(int id)
    {
        var entity = await context.People.SingleOrDefaultAsync(p => p.Id == id);
        if (entity == null)
            throw new InvalidOperationException("No person found matching id " + id);

        context.People.Remove(entity);
        await context.SaveChangesAsync();
    }

    public async Task UpdateAsync(Person item)
    {
        if (item == null)
            throw new ArgumentNullException(nameof(item));

        context.People.Update(item);
        await context.SaveChangesAsync();
    }
}

You should now have a file with the contents like this.

Now and explanation from the top down:

That’s it for our repository, now on to the Web API Controller

The Controller

Before implementing the methods, we need to import some namespaces and to create a new constructor that gets the PersonRepository injected:

using ProfileApi.WebApi.Data;
using ProfileApi.WebApi.Models;
private readonly IPersonRepository repository;
public PeopleController(IPersonRepository repository)
{
    this.repository = repository;
}

Above we are also saving a reference to the instance locally in our PeopleController.

Now we are ready to implement the the Get method for getting all people:

// GET api/people
[HttpGet]
public IActionResult Get()
{
    return Ok(repository.GetAll());
}

Peace of cake - we are just calling our repository. Nothing special here.

// GET api/people/5
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id)
{
    var entity = await repository.FindAsync(id);
    if (entity == null)
        return NotFound();

    return Ok(entity);
}

Here we check if the id matches, if it does we simply return HTTP 200 OK and return the Person as the JSON represantation. If not, we return HTTP 404 Not Found by calling the action method NotFound

// POST api/people
[HttpPost]
public async Task<IActionResult> Post([FromBody]Person model)
{
    if (model == null)
        return BadRequest("Please provide a Person.");
    if (string.IsNullOrEmpty(model.Email))
        return BadRequest("An email address is required.");

    var entity = await repository.AddAsync(model);

    return CreatedAtRoute(nameof(Get), entity.Id, entity);
}

We are doing some basic input validation and again using an action method to return a HTTP 400 Bad Request if the person model is null and if the email property is not set. We could add additional checks if we needed to. Then we create the entity and store it in our database by calling the repository method AddAsync.

Special case here is that we return a HTTP 201 Created with a path to the new resource. This is following REST best pratices.

// PUT api/people/5
[HttpPut("{id}")]
public async Task<IActionResult> Put(int id, [FromBody]Person model)
{
    if (model == null || model.Id != id)
    {
        return BadRequest();
    }

    var entity = await repository.FindAsync(id);
    if (entity == null)
    {
        return NotFound();
    }

    await repository.UpdateAsync(model);
    return new NoContentResult();
}

Here we are checking the id parameter and the model. Then we look for an existing entity in our database and if found we do an update against our repository.

// DELETE api/people/5
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
    try
    {
        await repository.RemoveAsync(id);
    }
    catch (InvalidOperationException e)
    {
        Console.WriteLine(e);
        return BadRequest("No Person found with id " + id);
    }

    return new NoContentResult();
}

Nothing that we haven’t already discussed in this method, except for some exception handling if the entity is not found.

So with all the above method in place in our PeopleController we are now ready to start our application and try it out.

Making Requests against our Controller

To easy things, I am going to specify the debugging/launch URL and port.

public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseUrls("http://localhost:6001")
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();

    host.Run();
}

We are adding the UseUrls() call to tell the host what url to use when debugging.

Run your app (Ctrl+F5 to run without debug, for faster launch time, if you don’t intend to debug)

Finally navigate to http://localhost:6001/api/People and you should see the 2 people we seeded in the database. Also, try out the other endpoints as specified in the top of this post. You should see something like this:

Get All People

That’s it - we are done for now. Next post will be about unit testing the controller and repository.

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket