.NET Framework, Core and Standard

Have been coding in .NET ecosystem for years, I know the term .NET Framework. I know that there are many versions of the framework from 1.0 to 4.x. Honestly, I did not care much about them. Well, because I have not had such a need.

Recently, there are “.NET Core” and “.NET Standard”. Officially, I have to deal with 3 terms. I must get my head around them. I must try to get a sense of them, as a developer.

.NET Framework

The latest documentation from Microsoft is here. There is so many useful information from the documentation page. But it also is too much to absorb. To absorb, I need a shorter version of the explanation. I need to draw a picture on my head. And here it is

.NET Framework High Level
.NET Framework High Level

Just in case you need a detail view, ask Google dot net framework architecture diagram. I cannot remember them. And most importantly I do not need to know that much.

Framework Class Library

Give us a huge amount of classes, API that we can use to build applications. When you build Windows applications, WPF, ASP.NET, … you user Framework Class Library. Class Library is a visual thing that you can use it directly. And most of the time, we just need to care about them. We, as developers, usually care

  1. Available API to do certain things, such as how to write to a file, open a file, how to write to Console, …
  2. Supported types, such as String, Int, Double, …
  3. How to create a class, a struct.
  4. How to iterate throw a collection such as for, foreach, … keywords

Common Language Runtime (CLR)

It guarantees this: Hey, do not worry. I got your back. Just do whatever Framework Class Library supports and we take care of the rest. As business application developers, we do not have to care about CLR. Of course, there is time CLR acts incorrectly or causes problems. But that is rare.

The CLR will handle the communication with underlying OS (which is Windows).

Key points

  1. Windows only. The code you write cannot run on Mac, Linux.
  2. Common Language Runtime (CLR)
  3. Framework Class Library

We use Framework Class Library to build our application, then CLR takes care of dirty job to ensure the code runs on Windows OS.

.NET Core

.NET Core is a general purpose, modular, cross-platform and open source implementation of the .NET Platform. It contains many of the same APIs as the .NET Framework (but .NET Core is a smaller set) and includes runtime, framework, compiler and tools components that support a variety of operating systems and chip targets. The .NET Core implementation was primarily driven by the ASP.NET Core workloads but also by the need and desire to have a more modern runtime. It can be used in device, cloud and embedded/IoT scenarios.

From official MS Documentation site.

There are many materials out there. There are videos, training. However, what important is how I read them. In my way, to ensure I understand, I must be able to draw an image in my head.

Let’s seek and extract information that I think they are important.

The first important bit is cross-platform. Put it simply our code can run on Linux.

NET Core High Level
NET Core High Level

Look pretty much the same as the .NET Framework with some differences

  1. More supported OS: Windows, Linux and Mac
  2. A smaller set of supported API. The “Class Library” box is smaller than in .NET Framework.

Gradually, the support API box will be bigger and bigger. Oh, one might ask why do I need to

Q: Oh, one might ask why do I need to use .NET Framework when there is .NET Core?

A: Because, the “Class Library” box of .NET Framework is a lot bigger. It will take a lot of time and effort to close the gap.

.NET Standard

The .NET Standard is a formal specification of .NET APIs that are intended to be available on all .NET runtimes. The motivation behind the .NET Standard is establishing greater uniformity in the .NET ecosystem. ECMA 335 continues to establish uniformity for .NET runtime behavior, but there is no similar spec for the .NET Base Class Libraries (BCL) for .NET library implementations.

From the official MS documentation. The key point here is “a formal specification of .NET APIs“. It is not an implementation. Its place in a big picture

NET Standard Big Picture
NET Standard Big Picture

From source here.

What it means is if your code use API described in NET Standard, it can be used in .NET Framework, .NET Core and Xamarin.

I want to express in my own way

NETStandardRunOn
Code adhered to NET Standard run on

Here is what NET Standard ultimately means

NETStandardContract
NET Standard is a contract

Therefore, when you develop a new feature, API and you want to reuse it many places. NET Standard is a good choice to make. Code again a Contract not an Implementation.

 

This is a high-level overview of the 3 concepts: .NET Framework, .NET Core, and .NET Standard. My objective is to draw images in my head about them. I understand them better in this way. I hope you can benefit from my way of viewing them.

With those images in mind, when someone prefers about one of them, I can immediately recall it. With the paradox of choices we have today, know what to take is important.

Knowledge and Skill “I Know vs I’ve Done”

Recently I watched a wonderful course on Pluralsight. It made me reflect again on my personal thought about Knowledge and Skill. For the past 2 years, besides my coding job, I have had the pleasure to interview and hire resources for my company. I have met many talented people. They know a lot of stuff. Some have many certifications. They are good. They have spent their time on getting them, taken exams, … No one can deny that fact.

In the context of working as a developer, tester, I mean building software, you are paid to do something. You are paid to bring your knowledge into practice, to use knowledge to produce something. You are paid for your Skill.

Skill is not built from what you know. Skill is built from what you have done. You gain skills by doing things. Of course, you must know things first. That is obvious, right?

Hey, but why do we care about differentiating between Knowledge and Skill? It might trigger a self-reflection of where we are, what value we bring to the marketplace. For me, It helps me tremendously. It forces me to think no matter where I am, I need to gain more skills, bring more value.

Let take an example of a web developer using ASP.NET MVC as their main tool for building web applications. He has 3 years experienced, counted since the first time he worked on an MVC project. Let’s examine what he knows and what he has done.

Knowledge – He Knows

By googling, reading some articles, reading some books about ASP.NET MVC framework, he gains a certain knowledge about

  1. What ASP.NET MVC is
  2. How to start an MVC project
  3. There are Controllers, Actions, Attributes, Routing, …
  4. Support Dependency Injection
  5. That MVC is a cool framework.

He knows a lot. When asked, he can cite from his reading. He even can compare MVC with other frameworks.

In the information age, you can gain knowledge quite easy. There are millions of materials out there, with both free and paid ones. If you want to know something, ask Google.

My friends, nowadays knowledge is free.

There are areas where people will pay for your knowledge (just make sure you have a solid one). However, a vast majority of developers is working in companies, writing code, producing software. Knowledge cannot produce software. Skill does.

Skill – He Has Done

In the interview, I usually ask what you have done precisely in the last 3 years. Which parts of the MVC framework that you have practiced most?

The answer I have received most is “I work on the business logic part. MVC has done everything else for me.” A very honest, correct answer! And many developers have done the same thing.

Given the Knowledge part, he knows ASP.NET MVC. However, he have not done any of these

  1. Code outside of a Controller – Action. Not create any custom attribute.
  2. Not knowing how to handle database transaction in an MVC application.
  3. Playing around with custom routing.
  4. Handling security

The list goes on.

The point is that he does not seem to have any ASP.NET MVC skill. How much value is he in the MVC field? As an employer, I think NOT much. There are so many developers know about ASP.NET MVC. But a few actually is able to use it. What the market needs are ones who can use it, not just know it.

 

Knowledge is unless it is used to solve problems. People are paid to solve problems. The takeaway key from differentiating Knowledge and Skill is we should be well aware of the illusion of knowing things. We have to gain more knowledge every day. At the same time, we have to find a way to improve our skills. They have to be in synced.

Next time, if you come to your boss and ask for raise, bring your skill list. Do not tell him what you know. Tell him what you have done and can do.

I am happy that I, now, have a clear thought about different between Knowledge and Skill, the importance of each. I am also glad that I have started my quest to skill with my Reload 2017.

Infrastructure and Application Code – Keep Loose Dependencies

When talking about dependency, many will think of Dependency Injection, think of Interface, think of Container, … Things go like if you have a Controller, that controller consumes a Repository, then you should inject them with dependency injection, most of the time via the constructor.

And we just stop at that level, at that thinking. Not so many ask for how to detect dependencies. Why do we have to think so much when things just work? No, we do not. However, it would be better if we dig deeper and understand more.

To identify dependencies, we have to define boundaries. Boundaries are the hardest thing to find, even for experts. There is no silver bullet. But there are some specific areas that we can identify and make a good decision.

Infrastructure and Application are 2 boundaries that I made up. They are too general, to be honest. Let’s start exploring with examples.

Application Settings

From my previous post, I have created an appsettings.json with .NET Core

{
  "AppSettings": {
    "Message": "Setting from app settings"
  }
}

And I have consumer code in AppSettingsController

    public class AppSettingsController
    {
        private readonly IConfigurationRoot _configuration;
        private readonly AppSettings _appSettings;
        private readonly GlobalSettings _globalSettings;
        public AppSettingsController(
            IOptions<AppSettings> appSettingsOptions, 
            IOptions<GlobalSettings> globalSettingsOptions,
            IConfigurationRoot configuration)
        {
            _configuration = configuration;
            _appSettings = appSettingsOptions.Value;
            _globalSettings = globalSettingsOptions.Value;
        }

        public Task GetAppSettings(HttpContext context)
        {
            return context.Response.WriteAsync($"Thie is AppSettingsController: {_appSettings.Message}; " +
                                               $"EF Connection String: {_globalSettings.ConnectionStrings.Ef}; " +
                                               $"Author: {_globalSettings.Author.Name}");
        }

        public Task GetDirectAppSettings(HttpContext context)
        {
            return context.Response.WriteAsync($"Direct app setting: {_configuration["AppSettings:Message"]}");
        }
    }

On the GetDirectAppSettings method, it accesses the setting from _configuration[“AppSetings:Message”]. It works. And look like we have Dependency Injection with IConfigurationRoot.

Then what are problems? What are differences between GetAppSettings and GetDirectAppSetings methods in term of accessing configuration values?

Problem

IConfigurationRoot, even an Interface, is Infrastructure Code, Infrastructure Concern. Whereas Controller is Application Code.

Let’s ask these questions:

  1. Does the AppSettingsController need to know about the present of IConfigurationRoot?
  2. Does it need to know about how to access a configuration value (such as AppSettings->Message)?

Asking questions is a good way to find out dependencies.

Asking questions is a good way to find out dependencies, to define boundaries.

I do not find “Yes” for those questions. Ok then. What does it need? It needs application settings values. It only needs values. And we accidentally tell it how to get them.

If not working with ASP.NET Core, we have ConfigurationManager class. Same problem! If you use it in your application code, you create a dependency.

Solution

In the ASP.NET Core example, the perfect way is reading configuration values at Startup

    public class Startup
    {
        private readonly IConfigurationRoot _configuration;
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json")
                .AddJsonFile("appComplexSettings.json");

            _configuration = builder.Build();
        }
        public void ConfigureServices(IServiceCollection services)
        {
            // Register the ability to read options in configuration
            services.AddOptions();
            services.Configure<AppSettings>(_configuration.GetSection("AppSettings"));
            services.Configure<GlobalSettings>(_configuration);
        }
    }

And controllers consume values via IOptions. Look at the AppSettingsController->GetAppSettings method.

A valid concern is that IOptions is also an Infrastructure code. In service code, we might not want to depend on IOptions to get the values. We might want to inject AppSettings directly. That should be a fairly easy task.

Impact

In a web application, for example, built with ASP.NET MVC, Controllers can access/accept that dependency with less impact. Because in fact, MVC itself is Infrastructure.

However, when it comes to service, domain, repository code, especially reusable components, it has a huge impact. Why? Because obviously, it depends on the infrastructure, the IConfigurationRoot for example. You cannot take those components and use other places where IConfigurationRoot does not exist.

It also creates another impact on developers mindset. Developers should develop a correct mindset while coding. They might have to think of dependencies, of boundaries, of portability. It is hard to get things right. But it is not hard to start thinking now.

Recap

Infrastructure and Application code is an example of identifying code boundaries. Asking questions is a good way to find them. I do not suggest you ask questions for any piece of code you write. I encourage you to start now. Create a habit of asking questions about your code. Start with methods, classes, then move on with Assembly level.

Try to look at things in boundaries, dependencies, instead of a big ball of mud.

ASP.NET Core Fundamental – Configuration

I started my learning journey with this ASP.NET Core. From my point of view, there are 3 major concepts I have to delve into: Configuration, Service, and Middleware. For each concept, I must learn the fundamentals; learn the things that will stick around with me; allow me to work on advanced features if I want to (of course, I will soon).

Let’s start with Configuration. If you are an advanced developer, you might want to read the full document at Microsoft docs. For me, I want to take a different approach. I want to start with my needs. I, then, extract the information I need, build the fundamental knowledge that is mine.

Goals

I want to know

  1. What kind of configuration options I have
  2. How to build a configuration file
  3. How to consume a configuration file
  4. How to deal with environments such as development, staging, and production
  5. How about deploying to Azure, how I change my configuration values on the fly.

Along the way, I might want to explore the flexibilities that I might have with .NET Core.

Code and Magic

Basic

I want to say “hello world” in a configurable way. Open VS2017, I add a new json file, named it appSettings.json

{
  "Message" :  "Hello word. This is Mr. JSON speaking." 
}

And consume the setting file

    public class Startup
    {
        private readonly IConfigurationRoot _configuration;
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appSettings.json");

            _configuration = builder.Build();
        }
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //app.UseMvc();
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync($"{_configuration["Message"]}. And you are? {context.Request.Path}");
            });
        }
    }

And it just works.

A Bit Deeper

There is more cool stuff that you could do with the JSON configuration. Should you need them all, refer back to the full documentation from Microsoft. I am only interested in the things that make more sense for common scenarios.

In a real application, the settings are more complex than single key-value pairs. Take a look at this

{
  "Message": "Override the simple message",
  "ConnectionStrings": {
    "ef": "Connection string to SQL database for EF"
  },
  "Author": {
    "Name": "Thai Anh Duc",    
    "Job": "Software Developer" 
  } 
}

How could I get a connection string for EF? Super easy with the support of accessing the child element with “:” separator

   public class Startup
    {
        private readonly IConfigurationRoot _configuration;
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appSettings.json")
                .AddJsonFile("appComplexSettings.json");

            _configuration = builder.Build();
        }
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //app.UseMvc();
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync($"{_configuration["Message"]}; " +
                                                  $"EF Connection String: {_configuration["ConnectionStrings:ef"]}; " +
                                                  $"Author: {_configuration["Author:Name"]}");
            });
        }
    }

I have added AddJsonFile(“appComplexSettings.json”) after the call to the first appSettings.json file. Let’s take a look at the output before continue

Override the simple message; EF Connection String: Connection string to SQL database for EF; Author: Thai Anh Duc
  1. The “Message” key has been overridden by the latest config file (appComplexSettings.json). The order matters.
  2. ConnectionString:ef“: navigate to the “ConnectionString” node, then get value from its child node: ef.

It is flexible and easy to code. But, I am in VS2017 with C# code. I want OOP style. For many reasons, the settings should be modeled with OOP fashion. Let model it

    public class ConnectionStrings
    {
        public string Ef { get; set; }
    }

    public class Author
    {
        public string Name { get; set; }
        public string Job { get; set; }
    }
    public class AppSettings
    {
        public string Message { get; set; }
        public ConnectionStrings ConnectionStrings { get; set; }
        public Author Author { get; set; }
    }

With the magic of .NET Core, let’s try to create a connection between the configuration and the model. Turn out it is pretty easy.

    public class Startup
    {
        private readonly IConfigurationRoot _configuration;
        private readonly AppSettings _appSettings = new AppSettings();
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appSettings.json")
                .AddJsonFile("appComplexSettings.json");

            _configuration = builder.Build();
            _configuration.Bind(_appSettings);
        }
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //app.UseMvc();
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync($"{_appSettings.Message}; " +
                                                  $"EF Connection String: {_appSettings.ConnectionStrings.Ef}; " +
                                                  $"Author: {_appSettings.Author.Name}");
            });
        }
    }

All I need to do is instantiate a new AppSettings instance, and then Bind to Configuration.

However, the Bind approach is manual. And in the production code, we might not use it. .NET Core supports the dependency injection with IOption. You can register it at the ConfigureServices step, and the use it everywhere. Let’s give it a try.

    public class AppSettingsController
    {
        private readonly AppSettings _appSettings;
        public AppSettingsController(IOptions<AppSettings> appSettingsOptions)
        {
            _appSettings = appSettingsOptions.Value;
        }

        public Task GetAppSettings(HttpContext context)
        {
            return context.Response.WriteAsync($"Thie is AppSettingsController: {_appSettings.Message}; " +
                                               $"EF Connection String: {_appSettings.ConnectionStrings.Ef}; " +
                                               $"Author: {_appSettings.Author.Name}");
        }
    }
    public class Startup
    {
        private readonly IConfigurationRoot _configuration;
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appSettings.json")
                .AddJsonFile("appComplexSettings.json");

            _configuration = builder.Build();
        }
        public void ConfigureServices(IServiceCollection services)
        {
            // Register the ability to read options in configuration
            services.AddOptions();
            services.Configure<AppSettings>(_configuration);

            // Register a controller with a lifestyle of httprequest scoped
            services.AddScoped<AppSettingsController>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //app.UseMvc();
            app.Run(HandleRequest);
        }

        public Task HandleRequest(HttpContext context)
        {
           var controller = context.RequestServices.GetRequiredService<AppSettingsController>();
            return controller.GetAppSettings(context);
        }
    }

There are a couple of things here

  1. Remove the appSettings variable, and instead use the services.AddOptions and services.Configure to register the AppSettings binding.
  2. Create AppSettingsController class. I just want to have a feeling of Controller here. And then register it with the services.
  3. In the HandleRequest method, resolve the Controller and delegate the work.

It works perfectly. Things are much easier than I did with Docker and deployment stuff 🙁

When we look at the HandleRequest again, it looks like the MVC framework started. Basically what it does is take a request (via HttpContext), process it, and prepare the Response object.

Deployment

Application settings are environment-dependent. Depending on either Development, Staging, or Production, keys have different values. Because .NET Core allows us to add multiple files. Order matters. Therefore, we can accomplish the goal with this code

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appSettings.json")
                .AddJsonFile($"appSettings.{env.EnvironmentName}.json")
                .AddJsonFile("appComplexSettings.json");

            _configuration = builder.Build();
        }

Whatever keys defined in the second appSettings file will override the default one (the appSettings.json).

The last question remains. How do I change values from Azure Portal?

First, let build and deploy it to the Azure portal using VSTS.

Welcome back to VSTS. Click Build. Build succeeded. Good. Click Release. Oops! Failed with “ERROR_FILE_IN_USE”. There is a long story discussed here in github. The quickest solution is to stop the App Service. Release completed. The result is the same as running locally. Cool.

Open the Azure Portal, look at the configuration of my App Service, seems there is no place to change values in JSON configuration. Let’s ask Google.

I have to make some modification to meet the convention structure.

{
  "AppSettings": {
    "Message": "Setting from app settings"
  }
}
    public class AppSettingsController
    {
        private readonly AppSettings _appSettings;
        private readonly GlobalSettings _globalSettings;
        public AppSettingsController(IOptions<AppSettings> appSettingsOptions, IOptions<GlobalSettings> globalSettingsOptions)
        {
            _appSettings = appSettingsOptions.Value;
            _globalSettings = globalSettingsOptions.Value;
        }

        public Task GetAppSettings(HttpContext context)
        {
            return context.Response.WriteAsync($"Thie is AppSettingsController: {_appSettings.Message}; " +
                                               $"EF Connection String: {_globalSettings.ConnectionStrings.Ef}; " +
                                               $"Author: {_globalSettings.Author.Name}");
        }
    }
    public class Startup
    {
        private readonly IConfigurationRoot _configuration;
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json")
                .AddJsonFile("appComplexSettings.json");

            _configuration = builder.Build();
        }
        public void ConfigureServices(IServiceCollection services)
        {
            // Register the ability to read options in configuration
            services.AddOptions();
            services.Configure<AppSettings>(_configuration.GetSection("AppSettings"));
            services.Configure<GlobalSettings>(_configuration);

            // Register a controller with a lifestyle of httprequest scoped
            services.AddScoped<AppSettingsController>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //app.UseMvc();
            app.Run(HandleRequest);
        }

        public Task HandleRequest(HttpContext context)
        {
           var controller = context.RequestServices.GetRequiredService<AppSettingsController>();
            return controller.GetAppSettings(context);
        }
    }

The AppSettings section will match the way web.config structured. Deploy to Azure portal again and see what happens.

Still cannot update the AppSettings directly in Azure Portal. This is a long post already. And I should call it a day (it is Friday night).

Recap

Even just a small step in the whole application development, when I get my hands on it, there are so many things I can learn from the design and implementation. The new configuration design gives us a lot of flexibilities. The system can access the configuration by using index access (by key with “:” separator), or build a configuration object (call as Option) and inject into many places. Whenever we want to use it, we can inject it. We get the dependency injection for free.

Because I have setup the deployment, I have a chance to use it. In short, I have a full lifecycle of development ready.

Happy Weekend!

 

ASP.NET Core with Docker Container – Part 2 Dockerfile

In ASP.NET Core with Docker Container – Part 1 Mounted Volume, I covered my experiment with Mounted Volume. In this post, I will explore the possibilities of Dockerfile. By reading on the internet, watching courses on Pluralsight, I know what Dockerfile is. “Know” means, well, just heard about it, saw people playing with it … on videos. Just like the first part, I want to explore it, do something with it.

I want to ship my application in a container. I want to be able to take that container and run it on another machine.

Dockerfile

I found a good article about it here by digital ocean. I feel it is too much to get started. And for a beginner, like me, it is hard to understand.

Dockerfile is a set of instructions to build a Docker image.

So the next question is what are the fundamental instructions to build an image? I always like to start with fundamental.

Fundamental Instructions

FROM

Instruct the base image we will use to build up. Docker gives you the ability to build your own image base on existing one. Think about it for a moment. We can take an existing aspnetcore-build image (which is made and maintained by Microsoft), and build our own image with some custom features.

FROM microsoft/aspnetcore-build

That’s all it takes to build your own custom image with full power of the aspnetcore-build.

MAINTAINER

It is nice to tell the docker that you maintain the image. It is you, the owner, the maintainer.

MAINTAINER thaianhduc

 

COPY

Start making your own custom image. The “COPY” instruction will tell Docker to copy a folder into a specific folder inside the container. It is the same concept as Mounted Volume, except, instead of creating a mapping, it copies the data.

COPY . /app

The above command will tell the Docker to copy the working directory into “app” folder inside the container. The equivalent in Mounted Volume is “-v ${pwd}:/app“.

WORKDIR

The directory that container will start. Think the same way when we open a Powershell or CMD in Windows. In our web application, it will be the web directory that we created in the COPY instruction.

WORKDIR /app

 

RUN

Given that the working directory, the container will run commands instructed by RUN. In our previous web application, we want to run “dotnet restore” and “dotnet run” commands. They might look like this

RUN dotnet restore

RUN dotnet run

Note: I might not want to build application inside the container. Most of the time, I want to ship my application, not source code.

EXPOSE

The port that a container exposes to the outside.

EXPOSE 8080

Which means other systems can communicate with it via port 8080.

ENTRYPOINT

All the above instructions tell Docker how to build an image. The ENTRYPOINT instruction tells containers what command to run once it starts. Let’s think about a web application scenario, once a container starts, we want to start our web server inside the container.

ENTRYPOINT ["dotnet", "Coconut.Web.dll"]

In Action

With the discovery from Dockerfile, I reframe my steps more precisely. I will/want

  1. Build an ASPNET Core Web Application on my host machine with VS2017 Community.
  2. Publish it to a local folder.
  3. Build a Docker image with the published application in.
  4. Start a container from my custom image.
  5. Access my web application from host browser.

Before started, I want to make sure that I have a working website published. This should be easy done by asking VS2017 to publish my project to a local folder. By default, it will publish to bin\Release\PublishOutput folder.

Website up and run well

So given at the published folder, when run “dotnet Coconut.Web.dll“, my website is up and run. So far so good.

How about recap the Mounted Volume approach? Which means I want to run this command

Recap: Run app with mounted volume

At the last line, I want to start an ASPNETCORE container to host my published application. Shall it work? Let’s find out.

It looks ok. Except, I cannot access the website from host browser. Which means something was wrong; well not a surprise at all. Turn out I made the wrong port mapping. It should be port 80 inside Container instead of 8080.

docker run -t -p 5000:80 -v ${pwd}:/app -w /app microsoft/aspnetcore bash -c "dotnet Coconut.Web.dll"

Create Dockerfile

A Dockerfile is a simple text file. You can create it with any your favorite editor.

# Custom image based on aspnetcore to host a web application
FROM         microsoft/aspnetcore
MAINTAINER   Thai Anh Duc
COPY         . /app
WORKDIR      /app
ENTRYPOINT   ["dotnet", "Coconut.Web.dll"]

For naming convention, I name it Dockerfile. I use VS2017.

Build a Custom Image

So far I have created a published application and a Dockerfile. Things seem ready. Next, we need to tell Docker to build an image from the Dockerfile.

The current folder structure looks like this

\bin
    \Release
            \PublishOutput
Dockerfile

Let’s try to run my very first build command. However, it failed right after build. Because that folder structure will cause some security issues. To make my life easy, I move the Dockerfile into the PublishOutput folder.

docker build -t thaianhduc/aspnetcore:0.1 .

Hey Docker, take the default Dockerfile at the current location (where the command is run), build an image, give it a name “thaianhduc/aspnetcore” and tag it with version “0.1”.

Do not forget the “.” at the end. It represents for “current location“.

// Run the command to list all the images. The new image should be there
docker images

Run It

Time to enjoy the fruit

docker run --name my-coconut -p 5000:80 thaianhduc/aspnetcore:0.1

Open the browser on my host machine, type in http://localhost:5000/hello_beautiful. Oh yeah! It works.

A Few Words

I have failed many times while experimenting and writing the post. Everything seems easy while you read it on the internet. However, when it gets down to actual do it. Evil hits your face at the first try. But that is actually a good thing. I have learned so much while trying to solve those problems. Some of them I understand, some I do not.

So far I have been playing around with the fundamentals of Docker. For every single command, for every single action, I try to explain to myself in my own language. The language that I can understand and explain to other.

Having small goals at the beginning keeps me focus. It allows me to temporary ignore some problems. Otherwise, you will wonder in the sea of information out there. Reading something is easy. Making something works for you is hard.

How do I take my custom image and use on other machines? Easy. I can push my image to Docker hub. Then I can run it just like other images. Things make sense now. I can resonate without effort.

Deployment Options Recap

I started with learning ASP.NET Core and other new cool stuff. I want to learn about the infrastructure that I can take advantages of. Let’s assume that I have a web application, how many options do I have to deploy it?

  1. Local host (IIS): Obviously I can simply press F5
  2. Deploy to Azure for free.
  3. Run it inside a container using Mounted Volume approach.
  4. Or deploy it as a Docker Image.

What’s Next?

I want to step further into the ASP.NET Core, from a fundamental point of view. Maybe later in the series, explore the possibilities of Docker in term of Microservices.

That would be fun!