Let’s Do a .NET Core Experiment

Christoph Bühler
smartive
6 min readApr 16, 2018

--

When I started at smartive, I ditched my backend engineering career. Mostly. I was still a big fan of static typed languages, even if I wrote JavaScript. By now, we write big applications with TypeScript and the benefits that came with it.

A little while ago, I stumbled across an old love of mine. C# and .NET (core). As the first version of .NET core was published, it didn’t came to my attention. With version 2, Microsoft took a big leap and created a stable, versatile framework that was worth investigating.

But still, it was “backend-technologies”. We have PHP and Symfony to create our applications at smartive, why invest in .NET? It’s Microsoft anyway. Since they released a cross platform framework that posed as Symfony like micro-framework, they may have a valid point. Or so I thought and tried a little something. It so happened, that we got a small project with a partner agency of ours and I had the pleasure to write some C# on top of the .NET core framework to access some data on a server, do some processing and expose the data to a GraphQL endpoint. This little assignment encouraged me to explore .NET core a little bit further. Here’s what I’ve gotten into.

FIFA 2018 - smartive - .NET core

FIFA is a passion, that some of us share. So I thought about an application, that tracks our progress, our games and derives a skill rating out of it to make a little competition.

Well I started a new web project in JetBrains Rider (which is a very nice IDE for writing C# based applications!) and structured my application. Back in the day we structured applications by their layers, so I thought “never change a running system”. I added entity framework core into a data layer, added a business layer and a web layer for presenting the information. Of course *cough* I added some test projects as well. Ok, actually I did, since I wanted to try that as well. The application setup was pretty simple, some web mvc logic, a little entity framework code first magic, google auth and here we are. The frontend parts are compiled with webpack and injected through a special HTML helper.

Small steps

So the first cool thing I notices was the simple way of adding authentication. I wanted google’s oauth so I can ignore user and role management. Well. It’s quite simple:

services
.AddAuthentication(GoogleDefaults.AuthenticationScheme)
.AddCookie()
.AddGoogle(options => /* configure google */);

Ok, what I left out, is the configuration part. But actually, you just need to add the client id and the client secret and you are ready to go. I changed some more settings, thus I’m not copying the whole source here.

As a goodie, if you want to limit your google auth to a certain domain, you need the following code:

options.Events.OnRedirectToAuthorizationEndpoint = context =>
{
var uri = new UriBuilder(context.RedirectUri);
if (!uri.Query.Contains("hd"))
{
uri.Query = uri.Query + "&hd=smartive.ch";
}
context.Response.Redirect(uri.ToString());
return Task.CompletedTask;
};

After I got the authentication running, I started coding my business layer and the tests.

Business Layer and it’s tests

The beauty is, you can code your business layer and if you did enough abstraction, you don’t need to run the application to test your code. Just use the integration / unit tests provided and you’re mostly good to go. Well I won’t describe the whole xUnit framework at this point, but there’s a little something that I want to share about unit tests and Entity Framework Core . Those of you, who don’t want to start a whole database and stuff for simple unit tests, you can use the Microsoft.EntityFrameworkCore.InMemory nuget package and start your context in memory!

public TestFifaDataContext(string name) : base(
new DbContextOptionsBuilder<FifaDataContext>()
.UseInMemoryDatabase(name)
.Options
)
{}

There is only one pitfall: If you initialise your in-memory-database with the same name (constructor) as another instance, it shares the same data. You need to clear the data after each run, or use randomisation to ensure a new instance each time.

Deployment

There isn’t much to say about deployment. I used gitlab-ci to create a multi-stage docker file and deploy it to our docker cloud. This will move to kubernetes in the future, since docker cloud is shutting down.

My little helpers

There were a few little helpers that made my life pretty easy:

  • Webpack (4 of course)
  • An active link tag helper (can’t believe that’s not in .NET-standard)
  • GetRessource Helper for webpack files

So the webpack config is a pretty normal (thus big) webpack config, and builds my typescript files and scssfiles into resources. Since those are hashed in production, I needed something that can get those files:

public static class HtmlHelperExtensions
{
public static string GetRessource(
this IHtmlHelper helper,
string resourceName,
string extension
)
{
var requestedFile = Path.Combine(
Startup.WebRootFolder,
resourceName
);
try
{
var dir = Path.GetDirectoryName(requestedFile);
var files = new DirectoryInfo(dir).GetFiles($"*.{extension}");
var file = files.FirstOrDefault(
f => f.Name.StartsWith(Path.GetFileName(resourceName))
);
return $"/{Path.GetDirectoryName(resourceName)}/{file?.Name}";
}
catch
{
return "";
}
}
}

With that helper, use the following code in your cshtmlfiles:

<link rel="stylesheet" href="@Html.GetRessource("css/vendor", "css")"/><!-- or --><script type="text/javascript" src="@Html.GetRessource("js/shared", "js")"></script>

Now in develop, files like js/shared.js are used, and if you compile your code into production, the same file named js/shared.2jd9fdd3.js is found and used instead.

And last but not least the active link tag helper, which could be better I assume, but it does the job for now:

[HtmlTargetElement("a", Attributes = "highlight-active")]
public class HighlightActiveLinkHelper : TagHelper
{
[ViewContext]
public ViewContext ViewContext { get; set; }

[HtmlAttributeName("match-exact")]
public bool Exact { get; set; } = false;

private readonly IUrlHelperFactory _urlHelperFactory;
public HighlightActiveLinkHelper(
IUrlHelperFactory urlHelperFactory
)
{
_urlHelperFactory = urlHelperFactory;
}
public override void Process(
TagHelperContext context,
TagHelperOutput output
)
{
var urlHelper = _urlHelperFactory.GetUrlHelper(ViewContext);
output.Attributes.Remove(output.Attributes["highlight-active"]);
var url = output.Attributes["href"].Value.ToString();
var currentRouteUrl = urlHelper.Action();
if (Exact && url != currentRouteUrl) return;
if (!Exact && !currentRouteUrl.StartsWith(url)) return;
var linkTag = new TagBuilder("a");
linkTag.Attributes.Add("class", "active");
output.MergeAttributes(linkTag);
}
}

The sole purpose is to get the url, check if it’s the given one, and add a css class to the element. Pretty simple, but obviously rocket-science in the eyes of a C# developer, since there is nothing compared to that in the default tag helpers.

Summary

TL;DR: 10/10 would use again.

So actually, despite the lack of certain helpers (maybe there is a library that I’ve not found yet) the convenience is overwhelming. Razor is a pretty neat and mighty template engine, which can do many things — although that does not mean you actually should! We all know, that an application does not belong into a frontend template. The static typing system is what made me happy in the first place and the tooling around the language and the framework is very nice as well.

Compared to other languages and frameworks like PHP (Symfony) and Node.js (express) it’s a very welcoming alternative. The compiled nature of .NET brings a nice performance boost. Compared to PHP the improvements in performance are quite immense, not so immense but still significant when compared to Node.js. Kestrel — which is the event based, self hosted web server of C# — is a valid alternative to Node.js and express. And one last piece (which is not directly part of .NET mvc): the EF Core. Entity Framework is a high performant alternative to Doctrine or any other ORM I encountered. EF does outperform many of them and produces pretty neat and damn well optimised queries.

It’s going to be a fun future with all those technologies. :)

--

--

Christoph Bühler
smartive

Code hero @smartive. Technology enthusiast, web engineer, Lindy Hop dancer and much more.