Splitting Up The Project

My code will be starting from where that post I mentioned at the start, so if you want to follow along a little better it might be worth doing a quick once over of that post. With that in mind, this is the project structure I’m starting with.

Starting file structure

Notice that we have our repositories, controllers, database, and view models all clumped together. The goal for this post is to move to a structure where our view models aren’t dependent on our database models, our controllers aren’t dependent on concrete repositories, etc.

New project breakdown

First we’re going to make two new projects called UI and Integration.EntityFramework and initialize a new project in each. The most tedious part of this process will be making sure all the new namespaces match up (though if you’re using an actual IDE like Visual Studio this should be much less of a problem), so make sure to spend some extra time fixing those file by file — I’m going to be omitting this part from the guide, hoping you’ll take care of this as we go. And as always, apologies in advance for any code formatting that gets butchered into weird multi-line messes.

Now that we have our new projects created, we should also add a global.json file that enumerates all the projects found in the root directory.

{

"projects":[

"Core",

"Integration.EntityFramework",

"Test.Unit",

"UI"

]

}

The UI project will hold things like our controllers, views, and view models. It will also become the entry point for the application, so all server configuration code will also be moving here.

The EntityFramework project will hold things like our repositories and database models.

While the Core project will lose the large majority of its contents, it will also gain some new things, namely all of our project’s interfaces and domain models. By the time we’re done, these new things will be the heart (or core) of our application with the new projects talking to each other as little as possible.

The Core Project

Since the Core project is the base of our application, it makes sense to start here. Add UserDomainModel.cs to the Models folder.

namespace Core.Models

{

public class UserDomainModel

{

public int Id { get; set; }

public string FirstName { get; set; }

public string LastName { get; set; }

public string Email { get; set; }

public string Password { get; set; }

}

}

Whether all these fields are necessary is arguable, but having them and then not needing them is almost definitely going to eliminate more headaches than it is going to cause. Remember that the domain model will not user facing, so we still have the opportunity to restrict what data we reveal when converting to a view model later. Then make an Interfaces folder and add IUserRepository.cs to it — this will be the blueprint for our UserRepository.

using Core.Models; namespace Core.Interfaces

{

public interface IUserRepository

{

UserDomainModel GetByEmail(string email);

void Save(UserDomainModel userToSave);

}

}

Notice that the interface only operates on the UserDomainModel. All business logic operations should be done using this model — when saving to the database we will map from a domain model to a database model, and when retrieving data from the database we will map from a database model to a domain model. Likewise when displaying data we will map from a domain model to a view model.

The Entity Framework Project

We now have the base for our other projects. Let us proceed with filling out the EntityFramework project next. Our project is going a project.json that looks like the following.

{

"version": "1.0.0-*",

"buildOptions": {

"debugType": "portable",

"emitEntryPoint": true

},

"dependencies": {

"Microsoft.EntityFrameworkCore": "1.0.0"

},

"frameworks": {

"netcoreapp1.0": {

"dependencies": {

"Microsoft.NETCore.App": {

"type": "platform",

"version": "1.0.0"

},

"Core": {

"target": "project"

}

},

"imports": "dnxcore50"

}

},

"tools": {

"Microsoft.EntityFrameworkCore.Tools": {

"version": "1.0.0-preview2-final",

"imports": "portable-net45+win8+dnxcore50"

}

}

}

Because this is the EntityFramework project it makes sense that the main dependencies are, well, Entity Framework. Take note of the frameworks section and where we list dependencies. The new entry here is where we’re telling this project to reference the Core project.

Create a new folder called Models and copy the UserDatabaseModel.cs and DatabaseContext.cs from Core/Models here. Before we start dealing with repositories, let’s get some infrastructure in place — we’re going to need some mappers to convert to and from the database and domain models.

Create a Mappers folder and add UserDomainModelMapper.cs to move from a database model to a domain model.

using Core.Models;

using Integration.EntityFramework.Models; namespace Integration.EntityFramework.Mappers

{

public static class UserDomainModelMapper

{

public static UserDomainModel MapFrom(UserDatabaseModel databaseModel)

{

return new UserDomainModel

{

Id = databaseModel.Id,

FirstName = databaseModel.FirstName,

LastName = databaseModel.LastName,

Email = databaseModel.Email,

Password = databaseModel.Password

};

}

}

}

And then create UserDatabaseModelMapper.cs to move from a domain model to a database model.

using Core.Models;

using Integration.EntityFramework.Models; namespace Integration.EntityFramework.Mappers

{

public static class UserDatabaseModelMapper

{

public static UserDatabaseModel MapFrom(UserDomainModel domainModel)

{

return new UserDatabaseModel

{

Id = domainModel.Id,

FirstName = domainModel.FirstName,

LastName = domainModel.LastName,

Email = domainModel.Email,

Password = domainModel.Password

};

}

}

}

These mappers might feel kind of redundant because the fields in both models happen to be the same in this case, but imagine a case where the database model has nullable fields. By mapping to a domain model be can use empty strings or zeros or whatever other convention you want to use in place of having to deal with null values (because null values are annoying and concrete types are almost always easier to work with).

Now that we have that out of the way we can copy the Repositories folder from Core and start fixing up the UserRepository. The main concern is making sure that the IUserRepository is properly implemented — our new mappers will be very helpful for this.

using System.Linq;

using Core.Interfaces;

using Core.Models;

using Integration.EntityFramework.Mappers;

using Integration.EntityFramework.Models; namespace Integration.EntityFramework.Repositories

{

public class UserRepository : IUserRepository

{

private readonly DatabaseContext _databaseContext; public UserRepository(DatabaseContext databaseContext)

{

_databaseContext = databaseContext;

} public UserDomainModel GetByEmail(string email)

{

var userFromDb = _databaseContext.Users.SingleOrDefault(x => x.Email == email);

return UserDomainModelMapper.MapFrom(userFromDb);

} public void Save(UserDomainModel userToSave)

{

var mappedDbModel = UserDatabaseModelMapper.MapFrom(userToSave);

var userFromDb = _databaseContext.Users.SingleOrDefault(x => x.Id == mappedDbModel.Id); if(userFromDb != null)

{

userFromDb.FirstName = mappedDbModel.FirstName;

userFromDb.LastName = mappedDbModel.LastName;

userFromDb.Email = mappedDbModel.Email;

userFromDb.Password = mappedDbModel.Password;

}

else

{

_databaseContext.Users.Add(mappedDbModel);

}

_databaseContext.SaveChanges();

}

}

}

These updates aren’t that substantial. In the GetByEmail method, we still take in a string that we search our database with. The important difference is that we’re mapping from that database model to a domain model.

Likewise in the Save method, we’re now taking in a domain model and mapping it to a database model, then doing our logic with this new mapped model. The frustrating thing about how this method changes is that we must set each field on the tracked database object so Entity Framework knows it has been updated.

I may have mentioned this before, but previous versions of Entity Framework had an AddOrUpdate function that did exactly what the Save method is accomplishing, but it has yet to be implemented in Entity Framework Core (which I’ve learned is actually a complete rewrite for .NET Core) as of the time I am writing this article.

At this point the EntityFramework project is finished and we can move on to fixing up the UI project. Don’t try to build yet because our application is horribly crippled and will scream out in pain.

The UI Project

As with the EntityFramework project, let’s start with what the new project.json file should look like for this project.

{

"version": "1.0.0-*",

"buildOptions": {

"debugType": "portable",

"emitEntryPoint": true,

"preserveCompilationContext": true

},

"dependencies": {

"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",

"Microsoft.AspNetCore.Mvc": "1.0.0",

"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-*",

"Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",

"Microsoft.EntityFrameworkCore": "1.0.0",

"Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.0"

},

"frameworks": {

"netcoreapp1.0": {

"dependencies": {

"Microsoft.NETCore.App": {

"type": "platform",

"version": "1.0.0"

},

"Core": {

"target": "project"

},

"Integration.EntityFramework": {

"target": "project"

}

},

"imports": "dnxcore50"

}

},

"tools": {

"Microsoft.AspNetCore.Razor.Tools": "1.0.0-preview2-final"

},

"publishOptions": {

"include": [

"Areas",

"Views",

"wwwroot",

"config.json",

"web.config"

]

}

}

Here notice we have dependencies on both the Core and EntityFramework projects. I know I said we want these projects to only touch the Core project, but I wanted to avoid solutions to this that seemed like hand-wavy magic until I more fully understood exactly what they were doing, especially for doing dependency injection (we’ll get to this toward the end of this section).

As you’ll see shortly, the UI project is really only reliant on the EntityFramework project in a few select places that deal almost exclusively with configuring our server. The effort to update these files in the event we decide Entity Framework is awful and we want to use some other technology is pretty low, so I’m not too worried about it currently.

Since this project is the new entry point for our server, let’s deal with all that nonsense first. Move Program.cs from Core. One caveat here is that Core still needs Program.cs as an entry point because it’s exposing its internals for our other projects to utilize.

To my understanding this has something to do with all projects being treated as console applications in .NET Core. I want to look into some workarounds or solutions to this because the Program.cs files feel pretty useless in the Core and EntityFramework projects.

For now we should have a UI Program.cs that looks like this.

using System.IO;

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Hosting; namespace UI

{

public class Program

{

public static void Main(string[] args)

{

var host = new WebHostBuilder()

.UseContentRoot(Directory.GetCurrentDirectory())

.UseKestrel()

.UseStartup<Startup>()

.Build(); host.Run();

}

}

}

And a Core Program.cs with almost nothing in it.

using System; namespace Core

{

public class Program

{

public static void Main(string[] args)

{

//required entry point

}

}

}

It’s annoying, but I’ve accepted it and moved on. If and when I figure out what the fix for this I will make sure to write about it.

Since appsettings.json is a really easy update let’s deal with that now. Move it from Core. Done — Startup.cs is next.

using System;

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Http;

using Microsoft.AspNetCore.Hosting;

using Microsoft.EntityFrameworkCore;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Configuration;

using Integration.EntityFramework.Models; namespace UI

{

public class Startup

{

public Startup(IHostingEnvironment env)

{

var builder = new ConfigurationBuilder()

.SetBasePath(env.ContentRootPath)

.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)

.AddEnvironmentVariables();

Configuration = builder.Build();

} public IConfigurationRoot Configuration { get; } public void ConfigureServices(IServiceCollection services)

{

services.AddMvc(); var connectionString = Configuration["DbContextSettings:ConnectionString"];

services.AddDbContext<DatabaseContext>(opts => opts.UseNpgsql(connectionString));

} public void Configure(IApplicationBuilder app)

{

app.UseMvc();

}

}

}

My issue with this file is how unwieldy it will get once the project becomes sufficiently complex. There are several ways to go about fixing this — I opted to build a helper class that will hopefully help make the code more readable now and going forward.

Create a Helpers folder and create StartupHelper.cs

using Microsoft.AspNetCore.Hosting;

using Microsoft.EntityFrameworkCore;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Configuration;

using Integration.EntityFramework.Models; namespace UI.Helpers

{

public static class StartupHelper

{

public static void AddDatabaseConnectionToServices(IServiceCollection services, IConfigurationRoot Configuration)

{

var connectionString = Configuration["DbContextSettings:ConnectionString"];

services.AddDbContext<DatabaseContext>(opts => opts.UseNpgsql(connectionString));

} public static IConfigurationBuilder GetConfigurationBuilder(IHostingEnvironment env)

{

return new ConfigurationBuilder()

.SetBasePath(env.ContentRootPath)

.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)

.AddEnvironmentVariables();

}

}

}

My goal is that each time I want to add something to the services in the future, I’ll be able to make a small ‘AddWhateverToServices’ method that will only have to be called in Startup.cs in a single place. Regardless of how that helper changes, the Startup.cs now becomes much more readable and transparent as to what its purpose actually is.

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Http;

using Microsoft.AspNetCore.Hosting;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Configuration;

using UI.Helpers; namespace UI

{

public class Startup

{

public IConfigurationRoot Configuration { get; } public Startup(IHostingEnvironment env)

{

var builder = StartupHelper.GetConfigurationBuilder(env);

Configuration = builder.Build();

} public void Configure(IApplicationBuilder app)

{

app.UseMvc();

} public void ConfigureServices(IServiceCollection services)

{

services.AddMvc(); StartupHelper.AddDatabaseConnectionToServices(services, Configuration);

}

}

}

Notice that Startup no longer has any Entity Framework dependencies and only relies on our Core project! Now if we swap out Entity Framework, we will only have to touch a single file in this project — the StartupHelper.

Now that we have our server code moved and updated, we can take care of the rest of those errors you’re likely getting when trying to build the project — the view models and controllers.

We’re going to need a new Models folder for the UserViewModel that can currently be found in Core/Models/View.

namespace UI.Models

{

public class UserViewModel

{

public string FirstName { get; set; }

public string LastName { get; set; }

public string Email { get; set; }

}

}

And a new Mappers folder for the UserViewModelMapper located in Core/Mappers. We’ll be adding a few updates to map from the UserDomainModel instead of the UserDatabaseModel we were previously using.

using Core.Models;

using UI.Models; namespace UI.Mappers

{

public static class UserViewModelMapper

{

public static UserViewModel MapFrom(UserDomainModel domainModel)

{

return new UserViewModel

{

FirstName = domainModel.FirstName,

LastName = domainModel.LastName,

Email = domainModel.Email

};

}

}

}

Almost there! To complete the UI project we still have to deal with the controllers and their views. Move the HomeController from Core/Controllers to a new Controllers folder, and the corresponding views from Core/Views/Home to an identical structure in this project — just make sure to update Index.cshtml so it’s using the correct model.

using System;

using Microsoft.AspNetCore.Mvc;

using Core.Interfaces;

using UI.Mappers; namespace UI.Controllers

{

public class HomeController : Controller

{

private readonly IUserRepository _userRepository; public HomeController(IUserRepository userRepository)

{

_userRepository = userRepository;

} [HttpGet("/")]

public ActionResult Index()

{

var email = "test@user.com";

var user = _userRepository.GetByEmail(email); var userViewModel = UserViewModelMapper.MapFrom(user);

return View(userViewModel);

}

}

}

Take note that we’re injecting an IUserRepository instead of using the DatabaseContext — we’re no longer coupled to the EntityFramework project and rely solely on Core for the definition of what our UserRepository will be able to do.

In the case where you’re injecting several different dependencies to a controller, you should consider adding a service later that encapsulates the repositories and the functionality you’re using them for. One or two dependencies is probably fine, but anything more than that is where I’d start considering this solution. By doing this you can simplify your controller again by having a single dependency and abstracting some of your logic to a different, possibly reusable location.

The last step before we can get the project and running again is to register our newly injected dependencies — we need to let the our application know what the heck to do with this IUserRepository when it’s injected into a controller. We’ll have to add a few small updates to StartupHelper.cs.

using Microsoft.AspNetCore.Hosting;

using Microsoft.EntityFrameworkCore;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Configuration;

using Core.Interfaces;

using Integration.EntityFramework.Models;

using Integration.EntityFramework.Repositories; namespace UI.Helpers

{

public static class StartupHelper

{

... public static void AddDependencyInjectionToServices(IServiceCollection services)

{

services.AddScoped<IUserRepository, UserRepository>();

}

}

}

We’re telling the application to user a UserRepository anywhere we’ve injected an IUserRepository. AddScoped is telling the application to only create one of these UserRepositories when it receives a request and then to hold on to it until it’s all done with its operations — if another request is made later, a new UserRepository will be created.

To put this work in action we need to update Startup.cs.

using Microsoft.AspNetCore.Builder;

using Microsoft.AspNetCore.Http;

using Microsoft.AspNetCore.Hosting;

using Microsoft.Extensions.DependencyInjection;

using Microsoft.Extensions.Configuration;

using UI.Helpers; namespace UI

{

public class Startup

{

... public void ConfigureServices(IServiceCollection services)

{

services.AddMvc(); StartupHelper.AddDatabaseConnectionToServices(services, Configuration);

StartupHelper.AddDependencyInjectionToServices(services);

}

}

}

At this point you should be able to launch the project and get literally the exact same output as you were getting before, except this time you have the satisfaction of knowing the code behind the page is markedly better in almost every way.