Contributing to the Microsoft ASP.NET Documentation My experience of writing for docs.microsoft.com

Back in February I spotted an issue on the ASP.NET Core Docs repository. The issue was a requirement for new documentation about the IHttpClientFactory feature being added in ASP.NET Core 2.1.

I’d been following the work on IHttpClientFactory for a while and had written a couple of posts about the functionality based on the nightly builds. I’d been keen to try my hand at helping with the docs.microsoft.com site for a while and this seemed like a great chance to contribute. In this post I’ll wanted to share my experience.

What is docs.microsoft.com?

Before I go further I should explain what docs.microsoft.com is. It’s Microsoft’s new (introduced in 2016) documentation portal, built from the ground up as a replacement for a number of prior documentation sites such as TechNet and MSDN. I believe it started with documentation for .NET Core / ASP.NET Core and has quickly grown to include documentation for many other Microsoft products and services.

One of the exciting and interesting changes is that many of the product teams have made their documentation open source. This allows members of the community to submit content and correct any mistakes they find. With more rapidly evolving products, keeping up is a real challenge. Allowing the community to assist is a great move and I believe has really improved the quality and usefulness of the content.

Additionally, an amazing team of managers and content writers has been assembled at Microsoft to build and maintain the improved documentation.

Getting Involved

So, back to my experience!

The first step was to show an interest and reach out to offer my contribution. I did this by commenting on the GitHub issue for the feature.

Scott Addie, a senior content developer at Microsoft who works on the ASP.NET docs team quickly responded to take up my offer. At this point we started to outline the content in collaboration with Ryan and Glenn from the ASP.NET team. You can follow that part of the story in the issue comments.

Once the plan and outline was agreed I was free to start work on the content.

Documentation

To get started, all I needed to do was to fork and clone the repository on GitHub. If those concepts are new to you, check out my YouTube playlist for some introductory videos on how that process works.

With the docs content on my machine I started a branch and begin outlining some content. All of the documentation is written in markdown. I prefer to use Visual Studio Code for markdown editing so I opened it up and created a document.

I began in the same was as I do for my blog posts, by outlining some initial sections and jotting down some notes. I like to organise my plan early on as it allows me to figure out a flow for the content and how I will build up information. Once the outline was in place I started to flesh out content. Often this required me to research the feature by trying out samples and reading the source code. Writing documentation (and blog posts) is a great way to learn more about a product or feature as you really look more closely than you might normally.

I made an initial pull request to the docs repository so that I could show the team my work and get feedback as I went. Being new to contributing I wanted to make sure I was on the right track. Scott was quickly on the case and offered some valuable feedback.

One of the big differences between writing for my blog and writing for the documentation is the style. In my blog I’m often referring to my personal experience and sharing with readers in a conversational style. For the docs the style is less personal and more concise. When reading documentation, users are often looking to get going quickly so producing tight, clear content is important. At first I found the switch in styles a little difficult, but after a while I was having to correct myself less often.

From there the work continued whenever I could get time to spend on the documentation. I was making contributions in the evenings, weekends and during my lunch breaks at work.

Building a Sample Application

One of the other requirements for good documentation is a sample application. These are often made available in the docs repository and are intended to provide a quick start for developers. Scott asked if it would be possible for me to produce the sample for the feature. The sample would also be used to support code snippets in the documentation. This is a really clever part of the docfx functionality which allows code snippets to be referenced using a special syntax in the markdown file. The code from the sample is then pulled into the documentation by a build process and stays up to date as the sample changes. The syntax even supports highlighting specific lines in the code for clarity.

Building the sample was a little complicated. We were just into the preview 1 of ASP.NET Core 2.1 at this stage, but some of the features of IHttpClientFactory hadn’t made it into that first preview. I was using nightly builds to experiment with the latest available functionality, but getting this working in a sample application proved complicated. I needed to match up the nightly SDK and ASP.NET Core builds to get things building and at times it would simply not build at all!

In the end we agreed that I would wait for the preview 2 release before putting too much time into the sample. Once preview 2 landed I was able to pick it up and build out the code.

Working out how to demonstrate things simply and accurately, but using code that would be suitable if coped and pasted was a careful balance. IHttpClientFactory is made up of a lot of extension method calls to configure the client instances. I found that before long I had a long list for the various options in my Startup class. Breaking these into snippet regions meant I could include the relevant samples in the documentation and break up the method a little.

During my trip to Seattle for my first ever MVP summit I was lucky enough to meeting Ryan Nowak, Glenn Condron and Damian Edwards from the ASP.NET team to chat about the feature and the documentation. During the MVP hack day I spent a bit of time refining the code sample and even got a live code review on one part of it from Glenn and Damian! Thanks guys!

Review and Refinement

Over a period of about 2 months I worked on the documentation and the sample application. It took longer than I expected mostly due to keeping up with the changes and fitting it into my free time. Finally though, this week I was pretty much done and ready for a review by the team.

A few changes were suggested including fixing up some grammar and spelling as well as some feedback on preferred styles and idioms for the sample. It was very valuable feedback and has turned my content into what I hope is some useful documentation.

You can follow the history and comments on the pull request.

Summary

I want to conclude this post with a huge thank you to the team at Microsoft. I was little nervous when I first started out, but was quickly put at ease by everyone I worked with. Scott in particular was very patient and helpful. Luke Latham also offered some great feedback to tidy up the content and language of the documentation.

Glenn and Ryan helped with some questions about the IHttpClientFactory feature as well as offering advice for the sample.

Thanks also goes out to Dylan from the App vNext team who maintain Polly who helped with clarifying some of the Polly functionality and offered some great last minute suggestions to tidy up a few sentences. Having so many people cast an eye over the content really helped tune it and make it clear and concise.

I’ve really enjoyed the collaboration and I have personally learned a lot along the way. It’s a great feeling to have contributed to something which I hope will help many developers start using IHttpClientFactory in their code.

If you’re thinking that this journey sounds fun, I recommend you check out the contributing guide and take a look for an issue you can help with. Even small fixes and corrections are most welcome and the team are a great bunch of people to work with.

Library Manager (LibMan) in Visual Studio 2017 (15.7) How to restore client side libraries in ASP.NET Core projects.

I recently started working on an ASP.NET Core 2.1 Preview 2 sample project. Having been mostly API focused recently, it was the first time that I’d done much with a site that actually renders views for a while. I found myself needing to re-learn my options for client side libraries.

My work on Humanitarian Toolbox is view based, however the project has grown up over a number of years and relies on a combination of NPM and gulp to bring in the required client side libraries.

For a brand new project, the templates will include a lib folder in wwwroot which has the necessary files which support the template features. However, if you’re checking into git, it’s quite normal to not include the lib folder, instead requiring the packages to be pulled on the client machine once cloned.

Full disclosure: I don’t consider myself a client side expert. I’m far more comfortable with the server side code! As such, this post is based on my own (potentially naive) approach to working with client side libraries.

Library Manager

Library Manager is a new feature included in Visual Studio 2017 (as of 15.7 preview 3) that provides new support for managing client side libraries in your projects. In this post I’m going to explore the basics of how I used this in a new project.

To add the Library Manager functionality to a project, simply right click on the project and choose the “Manage Client-Side Libraries…” option.

Manage client side libraries in Visual Studio 2017

This will add a single file to your project called libman.json. Note in the screenshot that at this point I don’t have a lib folder under wwwroot.

libman.json in solution

The libman.json file will be nearly empty when it’s first added. It includes a version and default provider. There is also an empty array of libraries defined. This is where we’ll add the packages we need for our project.

Default empty libman.json

In my example, my front end views only need bootstrap and jquery at this stage. Let’s look at how we can add those to our project. Each library is added as an object. There are a few properties we can set. The tooling offers autocomplete which makes populating this file a pretty straightforward experience. Here is an example of my final libman.json file.

Taking the boostrap entry as an example. The first value I provide is the name and version of the required library – “twitter-bootstrap@3.3.7”.

Next is the destination for the restored files relative to your project. In this case I’m including them under the wwwroot folder, in a directory called lib and then bootstrap.

Finally, in this example, I specify the individual files I want restored. This is optional. If you don’t include the array of files then all files from the library will be included. I preferred to be a little more selective about those that I needed here.

There’s also a value I’m not providing here to override the provider from which the library should be restored from. The provider options at this stage are cdnjs or filesystem.

Upon saving this file, the required libraries will be restored into your specified directory.

Restored lib folder in Visual Studio 2017

If you want to force a restore, perhaps after first cloning a project, you can do so by right clicking the libman.json file and choosing “Restore Client-Side Libraries”.

Restore client side libraries with lib man in Visual Studio 2017

Another option on this context menu is the “Enabled Restore on Build…” option. If you choose this option it will add a NuGet package to the project which will trigger the specified libraries to be restored on build. This is useful for CI / build servers for example (although I’ve not tested that at this stage). Choosing this option will present you with a dialog to confirm you wish to include the NuGet package.

Confirm adding Microsoft Library Manager build package

Once you do this a PackageReference will be added to you csproj file for “Microsoft.Web.LibraryManager.Build”.

CSPROJ after adding LibraryManager.Build

You’ll see the output from this when building your project. In this example I deleted the lib folder before triggering my build and you can see the files being restored as necessary.

Build output from libman.json (Library Manager)

That’s it for this post! I’ve not gone too deep into the tooling but so far this feels like a nice integrated way to specify and restoring client side libraries. Certainly I was able to get going with it pretty quickly and if it means I don’t need to learn about other client-side package managers and tooling, I’m pretty happy with that! I’m sure more seasoned client side developers will be better placed to judge this against the various other ways we can manage client side packages.

ASP.NET Core Anatomy – How does UseStartup work? Exploring how UseStartup results in your Startup methods being registered and executed.

I was recently explaining to someone the basics of the program flow for an ASP.NET Core application. One of the things included in the templates for ASP.NET Core and used very often is the UseStartup<T> extension method on the IWebHostBuilder. This gets called from our Program.cs when initialising the application. UseStartup allows us to set the Startup class which defines the services and middleware pipeline for an ASP.NET Core application.

During my explanation I realised that while I know the result of calling this method, I didn’t know how things are wired up under the hood; so I decided to investigate!

NOTE 1: This content is valid as at the 2.0.0 release codebase. I don’t expect that the fundamentals will change dramatically in the future but I have seen some commits which tweak the code a little for 2.1!

NOTE 2: This is a deep dive blog post looking at internal ASP.NET Core code. You don’t need to know this to use ASP.NET Core to build applications – please don’t let this scare you off! This is intended for those of you, who like me, have a curious mind about the internals of ASP.NET Core. I’m conscious that this may get quite hard to follow as we get deep into the guts of the code as there’s a lot of use of delegates that makes explaining the flow quite challenging. I’ll try my best to make it clear!

How are Startup methods registered and executed?

The generic UseStartup<TStartup> method calls down to the main IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType) extension, passing in the Type for the Startup class. That method looks like this (full source on GitHub):

The ConfigureServices method on the IWebHostBuilder is called which expects an Action<IServiceCollection> parameter. In this case it’s defined as a lambda expression which acts on the IServiceCollection. The WebHostBuilder class has a private List<Action<WebHostBuilderContext, IServiceCollection>> field named _configureServicesDelegates. The call to ConfigureServices in the code above will add the lambda as a new item to this list which will be used later to construct an instance of IStartup.

At this stage a list of delegates will have been registered within the WebHostBuilder. The main process begins when Build() is called on the WebHostBuilder. I won’t cover everything that Build does, since it’s not all relevant to the scope of this post. Here’s the code of that method (full source on GitHub):

The Build method calls a private method BuildCommonServices (full source on GitHub) which as the name suggests will add some common framework services into the current ServiceCollection. I’ll skip over most of the code in this method. Unless we’ve changed the WebHostOptions to define different assemblies to load Startup from, the execution flow will eventually hit code which looks over the _configureServicesDelegates List, calling each delegate in turn. That piece of code looks like this:

In the sample application which I used in order to debug through the Hosting codebase, I have two delegates registered, one from a FakeServer (needed so that the WebHostBuilder doesn’t throw an exception) and one from the UseStartup call. The first delegate simply registers the FakeServer as the implementation for IServer inside the ServiceCollection. The second delegate will now execute the lambda expression which was registered in the UseStartup method. Let’s remind ourselves what that looked like:

If our Startup class implements IStartup directly, it can and will be registered as the implementation type for IStartup directly. In my sample (which is based on the default ASP.NET Core templates) our Startup class does not implement IStartup and will rely on conventions instead. In this case an AddSingleton overload is used which takes the Func<IServiceProvider, object) as it’s implementation factory. This Func delegate will be called when the first concrete IStartup implementation is requested from the DI container.

At this point the registration of IStartup is included in the ServiceCollection and ready to be called by the framework. The WebHostBuilder.Build method continues to execute and constructs a new WebHost instance, which includes passing in an IServiceCollection (a clone of the current hostingServices variable). It also passes in a ServiceProvider, built using the current state of the hostingServices ServiceCollection. This represents the application services which have been registered so far by the framework.

Once we have a WebHost instance, its Initialize method is called. This calls down to a private BuildApplication method (full source on GitHub). Hold tight, lots of stuff starts to happen at this stage. I’ll try to pick out the parts I think are relevant to the use of our Startup class.

The BuildApplication method does some basic checks to make sure the relevant services are available for the application to start. One of the checks is that the ServiceProvider which was passed in includes an implementation for IStartup. This particular check happens inside a helper method called EnsureStartup()

The call to _hostingServiceProvider.GetRequiredService<IStartup>(); will trigger the DI framework to construct an instance of IStartup as per its registration. Due to the use of delegates, we need to head back to the UseStartup method to look at the lambda we passed in for the Func<IServiceProvider, object) implementationFactory. As a reminder, here’s the service registration that was used:

We can see that we’re going to get returned a newly constructed ConventionBasedStartup instance as the implementation for IStartup. The ConventionBasedStartup constructor accepts a StartupMethods object (full source on GitHub) as its parameter. A static StartupLoader.LoadMethods method (full source on GitHub) is used to generate a StartupMethods instance. This object acts as a holder for three properties.

The most important of these properties for this discussion are the delegates for the ConfigureServices and Configure. These will be setup with the code which should execute when the framework calls these methods further down in the WebHost initialisation. Ultimately the code for these delegates is expected to execute methods on our our Startup class.

The first delegate it will try to find is the ConfigureDelegate. This will be used to build the middleware pipeline for the application. Internally the StartupLoader uses a helper method called FindMethod to do most of the work. This is called from a FindConfigureDelegate method (full source on GitHub). The FindMethod is as follows (full source on GitHub):

This method will first work out the method name(s) it should be looking for on the Startup class based on the methodName parameter passed to it. The convention is that the method which defines the middleware pipeline should be called Configure. There is a lesser known convention in the Startup class which in addition to providing the standard “Configure” method, you can choose to include environment specific version(s) in your Startup class too. By convention, if a Configure{EnvironmentName} is found (e.g. “ConfigureProduction”) for the current environment, that method will be used in preference to the general Configure method.

In our sample, we only have the standard Configure method defined. Reflection is used to find a method matching the expected name on our Startup type. There are various checks in place to ensure that the expected members on our class are valid (i.e. we don’t have more than one Configure method defined with the same name) and that it has the expected Void return type.

Once we have the MethodInfo for the matching method, it is passed as the parameter into the constructor for a new ConfigureBuilder instance (full source on GitHub). This is stored in a variable in the LoadMethods method to be used a little later on.

A very similar process occurs to find and store the MethodInfo for ConfigureServices from our Startup class which is stored in a local variable called servicesMethod. Finally, the same approach is used looking for a ConfigureContainerDelegate. This is  an optional method which we can include on our Startup class to interact with 3rd party dependency injection containers such as AutoFac. We won’t look at this here.

Next, inside LoadMethods, a static ActivatorUtilities.GetServiceOrCreateInstance is called to get or create an instance of our Startup class. Here’s a compressed version of that LoadMethods method for reference (full source on GitHub):

As an implementation instance of IStartup is not currently stored in the DI container, GetServiceOrCreateInstance will create an instance of our Startup class by calling it’s constructor. In my sample (which matches the default Startup class in a new ASP.NET Core application template) it expects an IConfiguration object be passed in. The DI framework will have access to an implementation for this and will inject it in for us. Here’s my Startup constructor for reference:

Next the method on the ConfigureServicesBuilder is set as a callback variable. It gets passed the newly created Startup instance as its parameter. The same occurs to store a callback for ConfigureContainer. Next a Func<IServiceCollection, IServiceProvider> is setup using a lambda expression (which I’ve excluded from the code above for now). We’ll look at this when we see how this gets called a little later.

At this point, the callback delegates are passed into the constructor for a new StartupMethods instance which is then returned as the result of LoadMethods. This is then passed into the constructor for the new ConventionBasedStartup instance. At this point we have a concrete implementation of IStartup registered with the DI framework. Back inside the WebHost.EnsureApplicationServices method, the ConfigureServices method from the IStartup interface is called. Ready to start navigating some delegates!?

The ConfigureServices method on the ConventionBasedStartup instance calls the ConfigureServicesDelegate property on its StartupMethods member.

This executes the lambda defined code from the StartupMethods.LoadMethods method which in-turn invokes the Func<IServiceCollection, IServiceProvider> delegate returned from the ConfigureServicesBuilder.Build method, which ultimately calls the private ConfigureServicesBuilder.Invoke method (full source on GitHub).

The Invoke method uses reflection to get and inspect the parameters required by the ConfigureServices method defined on our Startup class. By convention this method can be either parameterless or take a single parameter of type IServiceCollection.

If the ConfigureServices method on our Startup class expects the IServiceCollection parameter, this is set using the IServiceCollection which was passed into the Invoke method. Once the method is configured via reflection it is invoked and the returned value will either be Void or an IServiceProvider. It’s at this point that we are actually executing the code contained in our ConfigureServices method on our Startup class. Our class can use the IServiceCollection extensions to register services and their implementations with the DI container.

At this point the lambda expression (in StartupLoader) wants to return a ServiceProvider. If our Startup.ConfigureServices method returned an IServiceProvider directly, this then gets returned immediately. If not, an IServiceProviderFactory is requested from the hostingServiceProvider and used to construct the ServiceProvider. This is the application level ServiceProvider that will be used to resolve dependencies in our code base.

The final point I’d like to show within WebHost.BuildApplication is how the final RequestDelegate is built. We won’t cover this in depth here, but in short, the RequestDelegate is defined as “A function that can process an HTTP request.” This is what the framework will actually use to process each request through our application. This will be setup to include all of the middleware as defined in our applications pipeline.

The relevant code in side of WebHost.BuildApplication is (full source on GitHub):

An IApplicationBuilderFactory is used to build up and finally surface our RequestDelegate. This is one of the services registered earlier in the WebHostBuilder.BuildCommonServices method.

The ApplicationServices property on the builder is set with the ServiceProvider that was just created. The next detail is something I’ll gloss over slightly as it goes a bit too far off the flow I want to explore. In short an IEnumerable of IStartupFilters may have been registered with the DI framework. In my sample I haven’t registered any so only the default AutoRequestServicesStartupFilter (full source on GitHub) will be returned from the ServiceProvider.

An Action<IApplicationBuilder> delegate variable is created holding a wrapped set of Configure methods from each IStartupFilter, the final one being the delegate for our Startup.Configure method. At this point, the Configuration chain is called which first hits the AutoRequestServicesStartupFilter.Configure method. This holds our delegate chain as its next action and so this will call down into the ConventionBasedStartup.Configure method. This will call the ConfigureDelegate on its local StartupMethods object.

Invoking that Action will call the private ConfigureBuilder.Invoke method (full source on GitHub) which looks like this:

This will prepare to call our Startup.Configure method sending in the appropriate parameters which get resolved from the ServiceProvider. Our Configure method can add middleware into the application pipeline using the IApplicationBuilder. The final RequestDelegate is built and returned from the IApplicationBuilder and the WebHost initialisation then completes.

Summary

This has been a quite long and deeply technical post. If you’ve stuck with it; well done! I hope I’ve interpreted everything correctly and lifted the curtain on some of the “magic” behind the scenes that makes ASP.NET Core work. It was quite difficult to explain everything due to the layers of delegates involved here. Hopefully I did a good enough job for you to get the gist of things. I find it really useful to dig into the code like this and gain a better understanding of the internals. If you want to explore the code yourself, check out the ASP.NET Core Hosting repository on GitHub.

Other posts in this series

Visit the ASP.NET Core Anatomy Index post to see the other deep dives covered in this series.

Docker for .NET Developers Header

Docker for .NET Developers (Part 1) An introduction to Docker for .NET developers

Two words you will very likely be used to hearing quite often within our community at the moment are “microservices” and “Docker”. Both are topics of great interest and are generating excitement for developers and architects. In this new series of blog posts I want to cover Docker, what it is, why it might be of interest and specifically look at what it means for .NET developers. As a little background; my experience with Docker started last year when we began building a new big data analytics system. The requirement was to gather many millions of events from hundreds of individual source systems into ElasticSearch, then provide a way for users to flexibly report over that data, in real time.

Without going deep into specifics, we developed a system of queues, input processors and multiple back end services which analyse the source data. These services work with the data, aggregating it, shaping it for custom reporting and we provide various API services used by a front end SPA UI written in Vue.js. In essence these are microservices since each one is small and self-contained, providing a specific set of related functionality.

We knew we wanted to use ASP.NET Core for the API elements and very soon after that decision, we realised we could also take advantage of the cross platform nature of .NET Core to support easier development for our front end team on their Mac devices.

Historically the front end developers have had to use a Windows VM to work with our projects written in .NET. They pull the latest platform code to their devices, build it and run it so that they can work on the UI elements. This process has some overhead and has been something that we have wanted to streamline for some time. With this fresh project we were able to think about and implement improvements in the process.

The solution that we came up with was to provide Docker images of the back end services that the front end developers could quickly spin up on their development environments. They can do this without the need to run a Windows VM and the result has been a great productivity gain.

As containers and Docker were working so well for use in development, we also decided to use them as our build and deploy process onto the live environment. We are using AWS ECS (EC2 Container Service) to run the containers in the cloud, providing a scalable solution to host the various components.

This is our first time working with Docker and it has been an interesting learning experience for everyone involved. We still have more to learn and I’m sure more ways we can improve the process even further but what we have now is already providing great gains for us.

What is Docker?

There are a lot of articles and videos available via a quick Google that discuss what Docker is. To try and distil the essence, Docker is a containerisation technology and application platform that lets us package and deploy an application or service as an isolated unit containing all of its dependencies. In over simplified terms it can be thought of as a very lightweight, self contained virtual machine.

Docker containers run on top of a shared OS kernel, but in an isolated way. They are very lightweight which is where they offer an advantage over traditional VMs. You can often make better use of the host device(s) by running more containers and better sharing the underlying resource. They have a lighter footprint, containing only the minimum dependencies that they require and they can share the host resources more effectively.

A Docker image can be as small as a few hundred megabytes and can start in a matter of a few seconds or even fractions of a second. This makes them great for scaling since extra containers can be started very rapidly in response to scaling triggers such as a traffic increase or growing queue. With traditional VM scaling you might have a few minutes wait before the extra capacity comes online, by which time the load peak could have caused some issues already.

Key Concepts

As this is an introduction post I wanted to summarise some of the core components and terms that you will need to know when beginning to work with Docker.

Image

A docker image can be considered a unit of deployment. Images are defined by the Docker files and once built are immutable. To customise an image further you can use it as the base image within your next dockerfile. Typically you store built images in a container registry which them makes them available for people to reference and run.

Container

A container is just a running instance of a Docker image. You start an image using the Docker run command and once started your host will have an instance of that image running.

Dockerfile

A dockerfile is how Docker images and the deployment of an application are described. It’s a basic file and you may only require a few lines to get started with your own image. Docker images are built up in layers. You choose a base image that contains the elements you need, and then copy in your own application on top. Microsoft provide a number of images for working with .NET Core applications. I’ll look into the ones we use in future posts.

The nice thing about using a simple text file to describe the images is that it’s easy to include the dockerfile in your repository under source control. We include various dockerfiles in our solutions that enable slightly different requirements.

Docker Compose

A Docker compose file is a basic way to orchestrate multiple images/containers. It uses the YML format to specify one or more containers that make up a single system, or part of a system. Within this file you specify the images that need to be started, what they depend on, what ports they should start under on the host etc. Using a single command you can build all of the images. With a second single command you can tell Docker to run all of the containers.

We use a docker compose file for our front end developers. We define the suite of back end components that need to be running for them to exercise and develop against the API services. They can quickly spin them up with a docker-compose run command to get started.

Host

The host is the underlying OS on which you will run Docker. Docker will utilise shared OS kernel resources to run your containers. Until recently the host would always have been a Linux device but Microsoft have now released Microsoft containers, so it’s possible to use a Windows device as a host for Windows based images. In our case we still wanted to support running the containers on Mac devices, so stayed with Linux based images. There are a couple of solutions to enable using the Linux images on Windows which I’ll go into more detail about in the future. In both cases, you essentially run Linux as a VM which is then your host.

Summary

This was a short post, intended to introduce Docker and some of the reasons that we started to use it for our latest project. ASP.NET Core and .NET Core lend themselves perfectly to cross platform development, and the use of Linux based Docker containers made sharing the back end components with our front end team a breeze. In the next posts I’ll go deeper into how we’ve structured our solutions and processes to enable the front end developers as well as showing building an example project.

Other Posts In This Series

Part 1 – This Post
Part 2 – Docker for .NET Developers Part 2 – Our First dockerfile
Part 3 – Why we started using Docker with ASP.NET Core
Part 4 – Working with docker-compose and multiple ASP.NET Core microservices
Part 5 – Exploring ASP.NET Runtime Docker Images
Part 6 – Using Docker for Build and Continuous Deployment
Part 7 – Setting up Amazon EC2 Container Registry

Things I’ve Learnt This Week (19th February)

Week 4 of my series, sharing things I’ve learnt, read, watched and listened to, in the pursuit of expanding my knowledge about software development. A slightly shorter set of links this week, things have been fairly busy so I’ve had less time to keep up with all of the fantastic content.

Things I’ve Learned

That I enjoy technical speaking! This week, I delivered a talk to a room of developers about ASP.NET Core. It’s a popular topic which I think generated the interest and high attendance. I’m quite shy and an introvert by nature, and I generally hate any form of public speaking so presenting a talk is way out of my comfort zone. However, it’s something I’ve been keen to work on; It’s a great way to share information and helps me learn as well. I had the usual nerves leading up to the talk, but I’d practised and refined it over a number of rehearsals, to the point that I was confident in what I was delivering. The talk also included a 30 minute live demo, which thankfully worked perfectly thanks to many practice runs. As soon as I got going I started to feel better and by the end, was on a bit of a natural high. I’ve had some very nice feedback from some of the attendees and I’m encouraged to work on future talks as well as hopefully sharing this one with a wider audience in the future. If I was asked for advice from other developers looking to prepare a technical talk, it would be practice, practice, practice! Having gone through my slides, rehearsing the full talk at least 10 times, I knew what I was going to say and that allowed me to present confidently.

Things I’ve Read

Things I’ve Listened To

Things I’ve Watched