These titles keep getting longer…

Hey folks! What up, what up? Let’s get back into our ASP.NET Core budgeting app.

In the last post in the series, we put together a small set of POCOs to be used with Entity Framework Core. Logs, Accounts, Payees, and Transactions. Everything we need to put together the basic personal finance functions of the app. You know when you log into your bank and you can click on your savings or checking account and see all the transactions? Yeah, that stuff.

In case you’re new here, I’m building the app in two waves in which the actual budgeting functionality gets added as a second version of the app. I’m doing it with a minimalist style of development where I only add the least amount of parts needed to get something working. Probably like what some startup folks do building an MVP.

Okay enough talk - now for this week. The Web API! ASP.NET Core’s bread and butter?

For this part, I put together a very basic set of API endpoints to exercise the functions I think I’ll need from the user’s point of view (just the personal finance part).

That means functions like:

  • creating a log (the parent structure for everything)
  • creating accounts (checking, savings, or credit card)
  • creating and deleting transactions

I created a DTO for each of my entities and I map to and from them using AutoMapper. The special sauce here is AutoMapper Linq Projections. I love clean code, and hence I love these projections, so this is what I wanted to highlight in this post.

[HttpGet]    
public IEnumerable<DTO.Account> Get()
{        
    return _dbContext.Accounts.ProjectTo<DTO.Account>().ToList();    
}

Let’s see how I did it:

1. Create the API Controller

Using the Web API project we created in the last series post in order to get entity framework to generate the database, we will add a new controller for handling finance accounts in the app. Right-click the Controllers folder then click Add -> New Item and choose the Web API Controller Class option.

We’ll call ours AccountController.

2. Create Data Transfer Objects (DTOs) to map to and from our database entities

I created a new folder in the root of the project called DTO and I name them the same as the entity they map with. Some people like to add DTO to the end of the name (like AccountDTO) but I prefer prefixing it like DTO.Account. I just think it looks nicer…

using System;
namespace YouReallyNeedABudget.WebApi.DTO
{
    public class Transaction
    {
        public int ID;
        public int AccountId;
        public DateTime Date;
        public int? PayeeId;
        public string Memo;
        public decimal Amount;
        public bool Cleared;
    }
}

3. Grab the AutoMapper and AutoMapper Dependency Injection Extensions from NuGet

Below is what I referenced in my project.json file. The second one lets us leverage one of my favorite parts about aspnetcore. We can inject automapper directly into our controllers without any of the global.asax.cs stuff or the App_Start configuration files that you might have to do in a Web Forms app, for example.

"AutoMapper": "5.1.1",
"AutoMapper.Extensions.Microsoft.DependencyInjection": "1.1.2"

4. Create an AutoMapper Profile for mapping between your DTOs and database models

I created a file in the root of the project called AutoMapperProfile.cs shown below.

using AutoMapper;
using YouReallyNeedABudget.Models;

namespace YouReallyNeedABudget.WebApi
{
    public class AutoMapperProfile : Profile
    {
        public AutoMapperProfile()
        {
            CreateMap<Log, DTO.Log>();
            CreateMap<DTO.Log, Log>();

            CreateMap<Account, DTO.Account>();
            CreateMap<DTO.Account, Account>();

            CreateMap<Transaction, DTO.Transaction>();
            CreateMap<DTO.Transaction, Transaction>();

        }
    }
}

5. Inject AutoMapper and the Budget DBContext so it can be used in the controller

In your Startup.cs file, use the .AddAutoMapper() extension which will automagically find the profile we created in the last step and set it up for use in our controllers. How’s that for clean code?

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();
            services.AddDbContext<BudgetContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
            services.AddAutoMapper();
        }

6. Finally, use LINQ projections with your API endpoints to keep your code tidy af

Don’t forget to add the using AutoMapper.QueryableExtensions; at the top!

using System.Linq;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using YouReallyNeedABudget.Models;
using YouReallyNeedABudget.DataAccess;

namespace YouReallyNeedABudget.WebApi.Controllers
{
    [Route("api/[controller]")]
    public class AccountsController : Controller
    {
        private readonly BudgetContext _dbContext;
        private readonly IMapper _mapper;

        public AccountsController(BudgetContext dbContext, IMapper mapper)
        {
            _dbContext = dbContext;
            _mapper = mapper;
        }

        [HttpGet]
        public IEnumerable<DTO.Account> Get()
        {
            return _dbContext.Accounts.ProjectTo<DTO.Account>().ToList();
        }
    }
}

That’s all folks!

Hopefully that provides some value to y’all. I’m digging all this aspnetcore stuff so far.

For brevity I left out a lot of stuff like model validation, logging, authentication. I also may change the routing a bit, for example it might be better to have it like /api/accounts/{accountID}/transactions so that I could remove the accountID from the transaction DTO… What do you think?

By the way, you can see the code in the github project. Take note that the latest code is in the develop branch, not always master.

If you liked it - please share the URL and/or sign up for my newsletter to follow along. I’ll give you a heads-up when the next post comes out. In it I’ll be adding a new project for the client and setting up ReactJS!

Other series posts: Part 1, Part 2, Part 4, Part 5