Structuring a .NET Core application – Part 1

Separation of Concern

Note, this series is very theoretical, and this first part especially, and might be for less experienced developers, but there should be something for everyone.

There are many ways to build an application, many right ways to build one and many wrong ways. I’m not the one to say the way you build your applications are wrong. I might be wrong; I might be right and so can you. It all depends on how you work best in your organization and under your circumstances.

With this post, I’m going to describe what I think is the best approach to application development anno 2020. Namely splitting up your code into separate blocks and services.

Previously, when I was less experienced as I am today, I used to build applications in large chunks. It’s easy. I need an application that takes X and splits it into A, B and C. A is going into a database, B and C is going to different external services.

Large methods

So, my application would have one large method, Save(X). That method would handle everything: Open a connection to the database, save A, close the database connection. Then, open a connection to External Service 1, post B, close the connection. And finally open a connection to External Service 2, post C and then close the connection.

It’s quite simple. Everything is there. My method does exactly as is advertised, (sort of), and it does get the job done.

Then, the next day, I need another method to only send B to External Service 1. Well, that’s easy. Open a connection to the service, post B and then close the connection again. Easy. Everything is fine, everything is working as expected and everyone is happy.

But one day, External Service 1, updates their API. There are new URL’s and a new authentication model. Everything in my application breaks. Who can fix it? I might, but it’s been a while since I’ve worked on that application, so my memory is a bit rusty. So, I try to remember all the places where I call the service and update to the new API. I might miss a few places, but those places are the ones not easily tested, so errors might only occur after a while. Had one of my colleagues gotten the job, the chance of failure would be very high!

This pattern of copying code from one place to another, is called duplicate code. In my experience, duplicate code occurs when you just need to add a feature or fix an error, quickly. Or, it might happen when the developer lacks the experience to foresee problems ahead of time. The latter certainly was the case for me for some time. Later it was the former.

If you need to copy and paste code, you’re doing something wrong.

Søren Spelling Lund, CPO, uCommerce

I’ve heard this quote many years ago, but it has stuck with me ever since, and I try to do my best not to copy and paste code. Well, we all copy and paste from Stack Overflow, but you know what I mean.

One of the problems with code duplication, besides the difficult maintainability, is that it, more often than not, results in huge methods and classes that attempts to do everything at once. Take our Save(X) example above. It does three things at once. It makes sense, since it has to split X into separate parts and send those parts to different services. But it has way too much responsibility. It has the responsibility to package and serialize the data into the formats each of the services requires. It also has the responsibility to open and close connections to each service and it has the responsibility to handle errors returned from each service. And most importantly, none of it can be reused. It has all that responsibility, and can only use it for that very specific task that is to save X.

Save(X) might look something like this:

public void Save(X){  
    // Split X into A, B and C  
    // Open the database connection  
    // Map A into a database entity  
    // Close the database connection  
    // Open a connection to External Service 1  
    // Serialize B into json  
    // Send json to External Service 1  
    // Close the connection to External Service 1  
    // Open a connection to External Service 2  
    // Serialize C into XML  
    // Send XML to External Service 2  
    // Close the connection to External Service 2  

What we need, is to relieve Save(X) of some of its responsibilities. Save(X) should only have one responsibility: Save X.

This is called Separation of Concern.

We need to identify every small part of the application and separate them into small reusable snippets. Where each snippet has one responsibility. And only one.

Again, let’s take our Save(X) example from above.

It can be split into 3 parts, which again can be split into, at least, three parts each.

Separation of Concern

What we need to do, is to separate everything into small, easily reusable and maintainable snippets with as few responsibilities as possible. This is called Separation of Concern.


Save(X), needs to store data into the database. We can separate that logic into a library that does that. The library has three responsibilities: Manage database connections, package/transform/serialize the data and store it in the database.

The first part is building a class that only has one concern: Database connection, let’s call it DatabaseConnection. It has two methods: Open () and Close (). This is a very simplistic setup and mostly theoretic, bear with me on this.

It might look like this:

class DatabaseConnection  
    public OpenDatabaseConnection Open()  
        // Open the connection.  
    public void Close()  
        // Close the connection.  

Open () returns a database connection that can be used to send data to/from the database. Our Save(X) could just use this and be over with it. Save(X) no longer has the responsibility of knowing how to open a database connection or how to close it. But it still has the responsibility of knowing how the data is formatted and sent to the database.

Therefore, we need another class: UnitOfWork, (again, simplistic and theoretic. More details in upcoming posts).

This class has the responsibility to send data through an open database connection with a method called SaveData(). And since we’ve just made a class with the sole responsibility of maintaining the database connection, our UnitOfWork class can utilize this class. If we choose to make a new UnitOfWork, we can make use of the same database context class and not have any duplicate code between our two unit of works, other than calls to the Open() and Close() methods.

Our UnitOfWork class could look like this:

public class UnitOfWork  
    private readonly DatabaseConnection databaseConnection;  
    public UnitOfWork(DatabaseConnection databaseConnection)  
        this.databaseConnection = databaseConnection;  
    public void SaveData(string tableName, object data)  
        // Save the data    

We could add a third layer, called Repository, (I know, using Entity Framework, NHibernate and the like, the repository pattern is redundant). What this class does is package the data and send it to the database. And since we’ve just made a class that has the responsibility of sending data to the database, our Repository only has one concern: Package the data. And then send that data to our UnitOfWork:

public class Repository  
    private readonly UnitOfWork unitOfWork;  
    public Repository(UnitOfWork unitOfWork)  
        this.unitOfWork = unitOfWork;  
	public void SaveA(AModel data)  
		// Map A into a database entity.    
		this.unitOfWork.SaveData("dto.A", mappedData);  

We have now separated the concern of managing the database connection, mapping to a database entity and saving that entity to the database, away from our Save(X) method, it now looks like this:

public void Save(X){  
	// Split X into A, B and C  
	// Open a connection to External Service 1  
	// Serialize B into json  
	// Send json to External Service 1  
	// Close the connection to External Service 1  
	// Open a connection to External Service 2  
	// Serialize C into XML  
	// Send XML to External Service 2  
	// Close the connection to External Service 2  

Much simpler, and we can now reuse Repository.SaveA() multiple places without thinking about changes to the database connection, database schema or future development.

Service clients

As with the database abstraction we did above, we can also split our service clients into separate parts with single responsibilities.

But first, lets break it down a bit.

We have, again, three responsibilities: Manage a connection to a service, serialize data and send data.

I feel lazy, so for the first part we’ll be using a build-in service called System.Net.Http.HttpClient. This service / client manages everything related to HTTP requests. Hence the name HttpClient. It handles opening and closing connections, so we don’t have to think about that. For now.

But we still need to serialize data before we can send it through the HttpClient.

We know we have to serialize at least two different kinds of data into, at least, two different kinds of string data, (JSON and XML). Let’s start by defining a reusable interface that does just that:

public interface IDataSerializer<TModel>  
    System.Net.Http.HttpContent Serialize(TModel model);  

This interface describes a service that converts a model of type TModel into an instance of HttpContent.

We can then create two services:

public class BSerializer : IDataSerializer<BModel>  
    public System.Net.Http.HttpContent Serialize(BModel model)  
        /// serialize to json. ... omitted for brevity  
        return new System.Net.Http.StringContent(jsonString, System.Text.Encoding.UTF8, "application/json");  
public class CSerializer : IDataSerializer<CModel>  
    public System.Net.Http.HttpContent Serialize(CModel model)  
        /// serialize to xml. ... omitted for brevity  
        return new System.Net.Http.StringContent(jsonString, System.Text.Encoding.UTF8, "text/xml");  

These two services each has one responsibity: Convert data to a format that can be sent to the service. Please note, these are very simplified examples.

We now handle the serialization part. What we need is a service that can send that data to our service endpoints. I like to call a service like that something like ServiceClient.

Our ServiceClient has two dependencies: HttpClient and an instance of IDataSerializer<TModel>. It also has a method called SendData that takes an instance of TModel and sends it through our HttpClient.

A such client could look something this:

class ServiceClient<TModel>  
    private readonly System.Net.Http.HttpClient httpClient;  
    private readonly IDataSerializer<TModel> serializer;  
    public ServiceClient(  
        System.Net.Http.HttpClient httpClient,   
        IDataSerializer<TModel> serializer)  
        this.httpClient = httpClient;  
        this.serializer = serializer;  
    public System.Threading.Tasks.Task SendDataAsync(TModel model){  
        var serializedContent = this.serializer.Serialize(model);  
        return this.httpClient.PostAsync("path to service", serializedContent);  

Again, very simplified. This client only handles a single model type and can only do HTTP Post requests. But then again, it’s for illustrative purpose only.

Please note, managing the HttpClient happens elsewhere. The serialization logic has also been moved elsewhere. The only thing this class does, is sending serialized data through the HttpClient.

Our Save(X) method above would then look something like this:

class SaveXample  
	private readonly Repository repository;  
	private readonly ServiceClient<BModel> externalService1;  
	private readonly ServiceClient<CModel> externalService2;  
	public SaveXample(  
		Repository repository,  
		ServiceClient<BModel> externalService1,  
		ServiceClient<CModel> externalService2  
		this.repository = repository;  
		this.externalService1 = externalService1;  
		this.externalService2 = externalService2;  
	public async System.Threading.Tasks.Task Save(XModel X)  
		await this.externalService1.SendDataAsync(X.B);  
		await this.externalService2.SendDataAsync(X.C);  

As you can see, much simpler. The only responsibility this class now has, is to split X into A, B and C. Saving A to the database is handle elsewhere. B and C is being handled elsewhere also. Some of the logic that handles B and C is reused. And it’s extensible. We can, quite simple, add a new service that sends C data as a binary stream by only creating a new IDataSerializer class.

The next part will be much more in dept into how I would organize and build libraries in a .NET Core application. Including how to wire up services like the ones described in this post and use dependency injection efficiently and reusable across applications.


Splitting code into small libraries, small classes and small methods. Has a lot of benefits:

1: Easier to maintain

It’s easier to maintain 10-20 lines of code doing one thing, than it is to sieve through 100s of lines of code that does everything to fix a bug.

It’s also easier to make changes without impacting the entire application. You can mark methods obsolete; you can change dependencies, interfaces and schemas without impact.

2: Reusability

When splitting your application into smaller pieces, you can reuse those pieces multiple places. And fixing an error in one of those pieces, means fixing it everywhere.

Reusability also opens up for unit testing. Having separated everything into separate parts, you can mock-up all dependencies and only test essential code.

You can even reuse your code across different applications, say an ASP.NET Core application and an Azure Function. Both can use the same library. And changes in that library will not impact either of them.

3: Scalability

By separating everything, you can build more efficient code, reducing memory footprint and CPU usage. You can implement caching strategies and manage disposables within each library / module / class / method.

4: Cons

By splitting everything into smaller pieces, one must be aware of changing the interfaces and contracts. Since the code you are changing can be used places you don’t know about.

A method that does one thing, must keep doing that one thing. All code that calls that method, expects it to do that one thing. Changing it to do another thing, is unexpected behaviour and might result in errors. Such changes must be made in a different method / class marking the old one obsolete if needed.

The developer must think of everything that depends on that library as external. It might be the developer him/her self that builds those applications, but after 4 weeks, he/she is definitely a different developer.

Making an ASP.NET Core front-end for WordPress – 1

I always try to find an excuse to code, I just love doing it. It rarely ends in a real life usable product, but I’m having a blast doing it.

For my latest project, I thought I would bring you around, just to get back into the blogging game, but also to keep my self on a schedule to, at least try to finalize this project.


It’s no secret, my blog runs on WordPress, it has done so since 2007. I have tried, many times to move to Umbraco, but I never really got there, so here we are.

This project, is a way for me to start that transition. I want to move all public content away from the CMS, into a standalone application based on ASP.NET Core. This way, I will be able to isolate my WordPress backend and have a greater control over my frontend.

I will also like be able to try switching to Umbraco Headless or a full, local, Umbraco install without changing the frontend or needing to rebuild the front-end completely.

I usually use Visual Studio for my programming, but I will try to make this post as “cross platform” as possible.

My project names are all prefixed with NDesoft, feel free to call yours what you want.

I will also use my own WordPress API endpoint, feel free to use that as well. Everything is cached by Cloudflare, so go nuts.

Resources / Documentation

I am using the following resources to help me along in this endeavor:


.NET Core CLI:

WordPress Rest API:


It must be based on ASP.NET Core.

Data access must be isolated from the web app, making it possible to add an App, Rest API or whatever later on without changing the code base.

CMS Api access must be isolated in a way that more can be added later on without rebuilding the applications completely from scratch.

This might make the project a bit over-engineered, but hey, we’re just having some fun here!

Getting started

First you must find a folder suitable for development. There, you open up a console, I prefer PowerShell, and run this command:

dotnet new sln

This creates a new Visual Studio solution file, nothing to it other than that.

Next we create a folder for our application, I call mine for “NDesoft.App”

In this folder we will create a new ASP.NET Core project, using the MVC template:

dotnet new mvc; dotnet sln ../NDesoft.sln add ./NDesoft.Web.csproj

This will create a new ASP.NET Core MVC project, and add it to our solution file.

Next, we need two more projects: One for wrapping the WordPress API, and one used as an interface between the web-app and the WordPress wrapper:

dotnet new classlib -o NDesoft.Wrapper;
dotnet new classlib -o NDesoft.Wrapper.WordPress;
dotnet sln add NDesoft.Wrapper;
dotnet sln add NDesoft.Wrapper.WordPress;

This will create two new folders, each with their own class library, (dll), project and add them to our solution file.

In each folder, the template has created a file called Class1.cs, please delete that. We don’t need that.

We’ll need to add some references between our newly created projects, to make them play nice together.

NDesoft.Web must have a reference to NDesoft.Wrapper and NDesoft.Wrapper.WordPress

NDesoft.Wrapper.WordPress must have a reference to NDesoft.Wrapper

To do that, we need to run the following commands:

dotnet add NDesoft.Web reference NDesoft.Wrapper NDesoft.Wrapper.WordPress;
dotnet add NDesoft.Wrapper.WordPress reference NDesoft.Wrapper;


We also need to install a few packages:

Newtonsoft.Json, AutoMapper and AutoMapper.Extensions.Microsoft.DependencyInjection

Automapper is used later on, but we might as well install it now.

dotnet add NDesoft.Web package automapper;
dotnet add NDesoft.Web package AutoMapper.Extensions.Microsoft.DependencyInjection;
dotnet add NDesoft.Web package Newtonsoft.Json;
dotnet add NDesoft.Wrapper package Newtonsoft.Json;
dotnet add NDesoft.Wrapper.WordPress package automapper;

And now, our solution and projects are up and running and we can start coding.


ASP.NET Core has native dependency injection, and since we are building a highly decoupled platform it makes sense to utilize that. So let’s start by adding a ton of interfaces. These will just be the bare minimum for this post, more will come.

In the NDesoft.Wrapper project, we’ll add a folder called “Interfaces” and under that another called “Helpers”.

In the Helpers folder, we’ll create to interfaces, (One file per interface):


This interface manages requests to the Rest API and deserializes the responds, I might add an interface for the deserialization, but for now it’s handled by this manager.

public interface IRequestManager
    Task<TResult> Get<TResult>(string url, CancellationToken cancellationToken, object parameters = default);


This interface helps us handle URL parameters in a shared way. No copy/pasting anything.

public interface IUrlParameterHelper
    string ConvertToUrlParameters(object parameters);

In the “Interfaces”-folder, we’ll create two new interfaces:


This is an interface that handles everything related to blog posts. It has nothing to do with authors, tags, categories, media, comments, pages and so on, only posts.

Please note the “PostModel”-class, this will be implemented in a bit.

public interface IPosts
    Task<IEnumerable<PostModel>> GetPosts(int currentPage = 1, int? pageSize = default, CancellationToken cancellationToken = default);


The name might change, I do not like the name, but for now it’s IClient. It’s the main entry point handling almost everything.

public interface IClient
    IPosts Posts { get; }


Now we have the basic interfaces going, let’s implement them.

First of we need a class that represents a single blog post:



Place this class in a folder, in the NDesoft.Wrapper project called “Models”.

public class PostModel
    public string Id { get; set; }
    public DateTime Date { get; set; }
    public DateTime Modified { get; set; }
    public string Slug { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public string Excerpt { get; set; }
    public string AuthorId { get; set; }
    public string FeaturedMediaId { get; set; }
    public bool IsSticky { get; set; }
    public IEnumerable<string> CategoryIds { get; set; }
    public IEnumerable<string> TagIds { get; set; }

This class is based off of the response from the following request for a single post:


The properties contains, what I think is, the minimum required number of properties to display a blog post. You might want more or less.

You might want to add a reference to this class in the IPosts interface.


In NDesoft.Wrapper create a folder called Helpers. In here, create a class called RequestManager:

public class RequestManager : IRequestManager
    public static readonly HttpClient httpClient = new HttpClient();
    private readonly string baseUrl;
    private readonly IUrlParameterHelper urlParameterHelper;
    public RequestManager(string baseUrl, IUrlParameterHelper urlParameterHelper)
        this.baseUrl = baseUrl;
        this.urlParameterHelper = urlParameterHelper;
    public virtual async Task<TResult> Get<TResult>(string url, CancellationToken cancellationToken, object parameters = default)
        var fullUrl = this.GetFullUrl(url, parameters);
        var response = await httpClient.GetAsync(fullUrl, cancellationToken).ConfigureAwait(false);
        var bodyContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        var result = JsonConvert.DeserializeObject<TResult>(bodyContent);
        return result;
    protected virtual string GetFullUrl(string url, object parameters = default)
        var parameterString = this.urlParameterHelper.ConvertToUrlParameters(parameters);
        return $"{baseUrl}{url}{parameterString}";

Worth noting: The constructor takes a string as first parameter. It contains the base URL of the Rest API. The value is, later on, set using app settings. However, I would like to not add it as a parameter, since it adds to the complexity of setting up the dependency injector.

Also noteworthy, this class has a static readonly HttpClient. This way we always use the same HttpClient for all requests. I do know about RestSharp and the like, but I prefer HttpClient as I thinks it’s easier to use.

I have a habit to always pass a Cancellation token into every async method that supports it. To me it makes sense because if a user aborts a request, every subrequest I make on my site will be aborted as well releasing resources.

It might be beneficial to add some caching here to reduce response times and load on the source server.


Next to the RequestManager, add a class called “UrlParameterHelper”:

public class UrlParameterHelper : IUrlParameterHelper
    public string ConvertToUrlParameters(object parameters)
        if (parameters == default)
            return string.Empty;
        var type = parameters.GetType();
        var properties = type.GetProperties();
        var urlBuilder = new StringBuilder();
        if (properties.Any())
            string joinedParams = GetParameterString(parameters, properties);
        return urlBuilder.ToString();
    private static string GetParameterString(object parameters, PropertyInfo[] properties)
        var urlParams = new List<string>(properties.Count());
        foreach (var property in properties)
            var value = property.GetValue(parameters);
            if (value != null)
                var stringValue = value.ToString();
                if (string.IsNullOrWhiteSpace(stringValue) == false)
                    var encodedValue = HttpUtility.UrlEncode(stringValue);
                    var urlParam = $"{property.Name}={encodedValue}";
var parameterString = string.Join("&amp;", urlParams.ToArray());
    return parameterString;

This class is used to help converting an object to url parameters, making it easier to create url’s with objects rather than string manipulation.

In the future, this class might be extended to handle number and datetime formatting better than it does now, as well as flattening nested properties.


And now, the last class in the NDesoft.Wrapper project, before moving on to the WordPress specific classes. This class should be located in the root of the project.

public class Client : IClient
    public Client(
        IRequestManager requestManager, 
        IPosts posts)
        this.RequestManager = requestManager;
        Posts = posts;
    protected IRequestManager RequestManager { get; }
    public IPosts Posts { get; }

There’s nothing much to this class at the moment, but we will add more in a future post.


Now we move on to some of the WordPress specific implementations.


This is not a class but a folder, we need to create a lot of classes that represents the json returned from the WordPress Rest API, and instead of me posting all those classes I’ll tell you how to generate all those classes.

First get the Json, I use Postman:


Copy the response body and:

In Visual Studio:

Edit -> Paste Special -> Paste JSON As Classes

Or use


In the root of the project, add a class called Posts:

public class Posts : IPosts
    private const string BaseUrl = "/wp/v2/posts";
    private readonly IRequestManager requestManager;
    public Posts(IRequestManager requestManager)
        this.requestManager = requestManager;
    public async Task<IEnumerable<PostModel>> GetPosts(int currentPage = 1, int? pageSize = null, CancellationToken cancellationToken = default)
        var parameters = new
            page = currentPage,
            per_page = pageSize
        var response = await requestManager.Get<IEnumerable<Post>>(BaseUrl, cancellationToken, parameters).ConfigureAwait(false);
        return Mapper.Map<IEnumerable<PostModel>>(response);

Some background here: The method “GetPosts” loads all posts from the API by calling this URL:


However, loading all posts at the same time would be overkill and slow down response times and over time only become slower. So, we need to add some paging parameters to the URL.

How to do that, we’ll look at this page it tells us that the API has two parameters we can use for our needs: “page” and “per_page”. Page, oddly enough is not zero-based so the first page is “1”.


Speaking of AutoMapper, we need some configuration to map the weird WordPress models the more leaner PostModel class. To do that, we need an AutoMapper profile.

Create a folder called “AutoMapperProfiles” and add a class called PostProfile:

public class PostProfile : Profile
    public PostProfile()
        CreateMap<Post, PostModel>()
            .ForMember(dest => dest.Date, m => m.MapFrom(src => src.date_gmt))
            .ForMember(dest => dest.Modified, m => m.MapFrom(src => src.modified_gmt))
            .ForMember(dest => dest.Title, m => m.MapFrom(src => src.title.rendered))
            .ForMember(dest => dest.Content, m => m.MapFrom(src => src.content.rendered))
            .ForMember(dest => dest.Excerpt, m => m.MapFrom(src => src.excerpt))
            .ForMember(dest => dest.AuthorId, m => m.MapFrom(src =>
            .ForMember(dest => dest.FeaturedMediaId, m => m.MapFrom(src => src.featured_media))
            .ForMember(dest => dest.IsSticky, m => m.MapFrom(src => src.sticky))
            .ForMember(dest => dest.CategoryIds, m => m.MapFrom(src => src.categories))
            .ForMember(dest => dest.TagIds, m => m.MapFrom(src => src.tags));

Again, nothing special here. All we do is we flatten out some of the weird constructs of the WordPress json and maps everything to a more lean model.


Now we have setup everything needed to access the WordPress API and have a solid base to build on. All we need to do now is wire everything up in the web project.

Configure AutoMapper

First things first, we need to configure AutoMapper. Open up the Startup-class and add the following method at the bottom of the class:

private void SetupAutoMapper(IServiceCollection services)
    services.AddAutoMapper(cfg =>

All we do here is adding AutoMapper to our services, and adds our PostProfile to the AutoMapper collection.

Setting up the Dependency Injector

Next up, adding our services to the Dependency Injector. Again in the Startup-class add the following method to the bottom of the class

private void SetupWrappers(IServiceCollection services)
    services.AddTransient<IUrlParameterHelper, UrlParameterHelper>();
    var baseUrl = Configuration["WrapperConfig:BaseUrl"];
    services.AddTransient<IRequestManager, RequestManager>((provider) => new RequestManager(baseUrl, provider.GetRequiredService<IUrlParameterHelper>()));
    services.AddTransient<IClient, Client>();
    // Service specifics:
    services.AddTransient<IPosts, Posts>();

All we do here is telling the DI, what services we have, and where to find their implementations. Note that everything uses the NDesoft.Wrapper classes, but only Posts uses the NDesoft.Wrapper.WordPress classes.

Before we’re done here, we need to add the following to the appsettings.json-file:


By doing this, we tell the wrapper where to find the endpoints needed to get the data.

Finishing up Startup

In the ConfigureServices-method, add these two lines at the top of the method:


And now we are set, and can start building our controller.


In the HomeController, remove everything except the Index-method and add the following:

private readonly IClient client;
public HomeController(IClient client)
    this.client = client;

This injects the client into the controller and we’re ready to use it.

Replace the Index-method with this:

public async Task<IActionResult> Index(CancellationToken cancellationToken)
    var posts =  await this.client.Posts.GetPosts(1, 5, cancellationToken).ConfigureAwait(false);
    return View(posts);

This code gets the first 5 posts from my blog and sends them to the view.

Final thing, we need to update the view. Open the file /Views/Home/Index.cshtml and replace everything with this:

@model IEnumerable<NDesoft.Wrapper.Models.PostModel>
@using System.Globalization;
    ViewData["Title"] = "Home Page";
@foreach (var item in Model)
        <a href="/@item.Date.ToString("yyyy/MM/dd", CultureInfo.InvariantCulture)/@item.Slug"><h2>@Html.Raw(item.Title)</h2></a>

Trying it out

Let’s try to run the darn thing:

dotnet build;
dotnet run --project ./NDesoft.Web;

You should now be able to go to http://localhost:5000 or https://localhost:5001 and se something like this:


The source code for this blog post can be found on Github:

Next part

In the next part, I’ll try getting pages, author, tags and categories up and running.