ASP.NET Core MVC – Doors Open, Come In!

Throughout the experiment, I have an empty project. Having an empty project is a good way to learn. It allows me to focus on other things first such as

  1. Build and Release with VSTS
  2. Play around with Docker and Part 2
  3. Fundamental – Configuration.

Ultimately, I want to use ASP.NET Core MVC to build a web application (and other services around it). Time to open the door and step into MVC world; but, as always, with the fundamental first.

ASP.NET Core application is enriched by the use of Middlewares at the developers’ will. MVC is one of the built-in middleware. Plug it in, you have MVC feature. Take it out, well, you are on your own.

MVC Middlewares High Level
MVC Middlewares High Level

Remember, order matters! Let’s take a quick tour with Middleware first.

Middleware

By default, even with an empty project, there are built-in middlewares auto generated. The detail is in the previous post.

Just plug the MVC Middleware in and we are good to go.

Model – View – Controller (MVC)

MVC Model High Level
MVC Model High Level

A request comes in, Controller does 2 things

  1. Build a proper Model.
  2. Decide what view to use.

Remember the keywords here: Build – Model, Decide – View.

The main purpose of a Model is to supply data for the View.

The View takes the data supplied from Model, and render the final HTML for the Response.

Why is that diagram important? Because It came from my understanding of MVC, in my own words (with some borrow from Courses I learned). The point is that I have my own way to express it instead of reading paragraphs of text. Diagrams, images increase my ability to explain.

When you learn something, try to draw diagrams. Try to explain it visually.

Show Me the Code

Let create a home page with some information from an empty project. From a design perspective, what do we need to build an MVC application? Well, I would naively say these

  1. Enable MVC middle
  2. Create a controller to handle the logic
  3. Create a view to display the result

With some fundamentals knowledge about MVC, I created these code and structure

AspNet MVC First Controller
AspNet MVC First Controller

Problems? When I hit F5, an empty page is displayed. I would have expected the Home/Index view displayed.

Old lesson: Nothing works at the first time. If it works, something must have been wrong.

I guess that I missed the routing. Basically, I have to tell the MVC framework how to map a request to a Controller.

Let dig a bit further into routing from MS docs site. There are so many things to learn from that site. However, I just want to get my first simple routing work. I pick what matters most to me.

       public void ConfigureServices(IServiceCollection services)
        {
            // Register the ability to read options in configuration

            services.AddRouting();
            services.AddMvc();
 
        }

        // 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(routes =>
            {
                routes.MapRoute(
                    "default",
                    "{controller=Home}/{action=Index}/{id?}");
            });
        }

To make the magic work, we must

  1. Enable both Route (AddRouting) and MVC (AddMvc) services. This will register all the necessary services required for routing and MVC
  2. Use MVC with configured routing. Above I defined a default routing.

Visit the home page. It works as I expected. Note

Note: If you create an MVC project from an MVC template, they are all created for you, for free.

Add Logic

The next step is to work with the logic code. So far, I have had a Controller ready to play around, the mighty HomeController.

Display an ugly fact: How far are you to the creator?

Let’s assume a man lives for 80 years maximum. Given a birthday, display how many you have spent and how close you are to the creator.

It is a pretty simple logic. The questions I want to explore here are

  1. Where should I put the logic code?
  2. What kind of project should it be? .NET Standard? .NET Core? or .NET Framework?
  3. How to organize data and flow?

First, let make the functionality work

    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }

        /// <summary>
        /// Serve the GET /home/uglyfact?birthday=dd-MM-yyyy
        /// </summary>
        /// <param name="birthday"></param>
        /// <returns></returns>
        public IActionResult UglyFact(string birthday)
        {
            // Ignore all data validation. Simply assume that the request is correct.
            var dob = DateTime.ParseExact(birthday, "dd-MM-yyyy", CultureInfo.CurrentCulture);
            var meetCreatorOn = dob.AddYears(80);
            var spent = (int)Math.Round(DateTime.Now.Subtract(dob).TotalDays / 7);
            var closed = (int)Math.Round(meetCreatorOn.Subtract(DateTime.Now).TotalDays / 7);
            return View( new UglyFactModel
            {
                SpentWeeks = spent,
                CloseToCreatorWeeks = closed
            });
        }
    }

There is an UglyFact Action which will do some simple calculation. And then it returns an UglyFactModel to the View. Pretty typical MVC stuff. When we take a closer look

When we take a closer look at the UglyFact action implementation, it is a typical piece of business logic. We know that it should not be there. The business logic should not be in the Controller-Action code.

The business logic should bot be in the Controller-Action code.

I decided to go with .NET Standard. Because the business logic code does not depend on the .NET Framework.

namespace Coconut.CoreLogic
{
    public class UglyFactTeller
    {
        public UglyFactModel TellMe(DateTime dob)
        {
            var meetCreatorOn = dob.AddYears(80);
            var spent = (int)Math.Round(DateTime.Now.Subtract(dob).TotalDays / 7);
            var closed = (int)Math.Round(meetCreatorOn.Subtract(DateTime.Now).TotalDays / 7);
            return new UglyFactModel
            {
                SpentWeeks = spent,
                CloseToCreatorWeeks = closed
            };
        }
    }
}

The logic in the Controller-Action has been moved to a separated assembly. And here the nice-clean controller

namespace Coconut.Web.Controllers
{
    public class HomeController : Controller
    {
        private readonly UglyFactTeller _uglyFactTeller;

        public HomeController(UglyFactTeller uglyFactTeller)
        {
            _uglyFactTeller = uglyFactTeller;
        }
        public IActionResult Index()
        {
            return View();
        }

        /// <summary>
        /// Serve the GET /home/uglyfact?birthday=dd-MM-yyyy
        /// </summary>
        /// <param name="birthday"></param>
        /// <returns></returns>
        public IActionResult UglyFact(string birthday)
        {
            // Ignore all data validation. Simply assume that the request is correct.
            var dob = DateTime.ParseExact(birthday, "dd-MM-yyyy", CultureInfo.CurrentCulture);
            return View( _uglyFactTeller.TellMe(dob));
        }
    }
}

To make everything works as before I tell the HomeController how to resolve its dependency (the UglyFactTeller class)

services.AddTransient<UglyFactTeller>();

What’s Next?

At the beginning, I wanted to write more in the post. However, it seems too much for a single post. It is a better idea to stop here and enjoy the “get the job done” feeling.

Next, I will continue with how to make the code structure better (with MediatR (mediator pattern)), explore the unit test.

One of the fun thing when doing from the scratch (or at least with an empty project) is that you think you know them but you do not. You do not know unless you start coding/implementing.

.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.

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!

 

%d bloggers like this: