ASP.NET Core 1.0

The model adopted for ASP.NET applications that use .NET Core is totally based on the previous MVC model. But it's built from scratch, with the target on cross-platform execution, the elimination of some features (no longer necessary), and the unification of the previous MVC with the web API variant; so, they work with the same controller type.

Besides this, the code doesn't need to be compiled prior to execution while you're developing. You change the code on the fly and Roselyn services take care of updating; so, you just have to refresh your page to see the changes.

If we take a look at the new list of templates, after installing .NET Core in the "Web" development section, we're offered a classic version of ASP.NET, where we have the typical templates you already know (including Web Forms applications) and two new options: ASP.Core Web Application (.NET Core) and ASP.NET Core Web Application (.NET Framework) (Review the first image at the beginning of the .NET Core 1.0 section to remember the architecture).

What's new

Many new things show up in this version of ASP.NET Core. First, there's a new hosting model because ASP.NET is completely decoupled from the web server environment that hosts the application. It supports IIS versions and also self-hosting contexts via Kestrel (cross-platform, extremely optimized, built on top of LibUv, the same component that Node.js uses) and WebListener HTTP (Windows-only) servers.

We also count on a new generation of middleware that are asynchronous, very modular, lightweight, and totally configurable, where we define things such as routing, authentication, static files, diagnostics, error handling, session, CORS, localization, and even you can write and include your own middleware.

Note

For those who don't know, middleware is a pipeline element that is run before and after the user code. The components of a pipeline are executed in a sequence and they call the next one in the pipeline. In this way, we can execute pre/post code. When a piece of middleware generates a Response object, the pipeline returns.

Refer to the following schema:

What's new

In addition, a new built-in IoC container for dependency injection is responsible for bootstrapping the system, and we also find a new configuration system, which we'll discuss in more detail a bit later.

ASP.NET Core joins many things that were separate earlier. No more distinctions between MVC and Web API, and a complete set of new Tag Helpers is available. And if you target .NET Core, or if you prefer to target any of the other versions of .NET, the architectural model is going to be MVC with this rebuilt architecture.

A first approach

Let's look at the structure of a project made up of the default templates available in Visual Studio 2015. You just have to select New Project | Web in Visual Studio to see these alternatives in action:

A first approach

I think it's a good idea to start with the simplest possible template and start digging into the programming architecture that lies behind this new proposal. So, I'll start with one of these new projects and select the Empty option. I'm offered the three initial choices: Empty, Web API, and Web Application.

A basic directory structure will be created for us, where we'll easily find some of the elements we previously saw in the introduction to .NET Core (including the separated global.json file used to define directories, projects, and packages). I named this demo ASPNETCoreEmpty (refer to the next screenshot for the solution structure).

You might be surprised to notice the absence (and also the presence) of certain files at first.

For instance, there's a new folder named wwwroot, which you surely know from other applications hosted in IIS. In this case, that hasn't to do with IIS: it only means that it is the root directory of our site. Actually, you'll also see a web.config file, but that's only to be used if you want the website to be hosted in IIS precisely.

You will also see the presence of a project.json file, but be careful with this. As the official documentation states:

"ASP.NET Core's configuration system has been re-architected from previous versions of ASP.NET, which relied on System.Configuration and XML configuration files like web.config. The new configuration model provides streamlined access to key/value based settings that can be retrieved from a variety of sources. Applications and frameworks can then access configured settings in a strongly typed fashion using the new Options pattern."

The next capture remarks the two main .cs files created by the project:

A first approach

Furthermore, the official recommendation is that you use a configuration written in C#, which is linked to the Startup.cs file that you see in the file structure. Once there, you should use Options pattern to access any individual setting.

So we now have two initial points: one related to the host and another that configures our application.

Configuration and Startup settings

Let's briefly analyze the file's contents:

// This method gets called by the runtime. Use this method to add 
// services to the container.
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, IHostingEnvironmentenv,
ILoggerFactory loggerFactory)
{
  loggerFactory.AddConsole();

  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
  }

  app.Run(async (context) =>
  {
    await context.Response.WriteAsync("Hello World!");
  });
}

You can see, there are only two methods: ConfigureServices and Configure. The former permits (you guessed it) to configure services. The IServiceCollection element it receives allows you to configure logins, options, and two flavors of services: scoped and transient.

The latter is the initial entry point of our application, and it receives three arguments that permit the developer all types of configuration and initial settings. The first one, loggerFactory, lets you add ILoggerProvider to the login system (refer to https://docs.asp.net/en/latest/fundamentals/logging.html for more details), and in this case, it adds the Console provider.

Note

Observe that these two method's arguments are received automatically. Behind the scenes,the Dependency Injection engine provides these and other instances' elements.

We can add as many logging providers as we want: each time we write a log entry, that entry will be forwarded to each logging provider. The default provider writes to the Console window (if available).

The following lines also explain some important things about the way this pipeline works. The second argument (of type IHosting Environment) lets you configure two different working environments: development and production, so we can, like in this case, activate error pages proper for development, or we can configure these errors in a customized manner. This argument also contains some utility properties for developers.

The third argument (of type IApplication Builder) is the one that really launches the application. As you can see, it calls the Run method of another object received by injection: the context variable (of type HttpContext), which holds all the required information and methods to manipulate the dialog process.

If you take a look, you'll see that it has properties such as Connection, Request, Response, Session, User, among others and an Abort method to cancel the connection at any time.

Actually, the code calls the Run method asynchronously (with async/await), and it writes content that is addressed to the clients. Note that no HTML is implied here yet. If you run the project, you will see the Hello World text as expected every time a request to a port is made over localhost. (The IDE randomly assigns a different port for each application and you can change that, of course).

So, you can change the Response object, adding some more information to the initial response. A look at the context object shows several properties related to the process, such as the Connection object, which has a Local Port property whose value we can add to Response by just modifying the code in this way:

app.Run(async (context) =>
{
  string localPort = context.Connection.LocalPort.ToString();
  await context.Response.WriteAsync("Hello World! - Local Port: " + localPort);
});

However, we said we can change the hosting context. If we select the running host to be the name of our application instead of IIS Express, then we're opting for the self-hosting option, and two windows will open at runtime: a Console window (corresponding to the host) and the browser we select, sending a request over the application.

So, we should see one console with the data related to the hosting, as shown in the next screenshot:

Configuration and Startup settings

Simultaneously, the selected browser will open, showing the initial message plus the modified information, including the port number:

Configuration and Startup settings

Note that although a look at the code seems to have some markup, this is included in the browsers because when they receive plain text, they wrap it around some basic markup instead of just presenting the text without any HTML. But we still didn't activate the option to serve static files.

Also, observe that there's no checking of resources; so, no matter what you put next to the localhost:5000 address, you'll get the same result.

On the other hand, the host construction is made in the Program.cs file, where we find the entry point, which only creates a new host that calls the constructor of WebHost Builder and configures some default behavior:

public static void Main(string[] args)
{
  var host = new WebHostBuilder()
  .Use Kestrel()
  .Use ContentRoot(Directory.GetCurrentDirectory())
  .Use IIS Integration()
  .Use Startup<Startup>()
  .Build();
  host.Run();
}

If you take a look at the WebHost Builder class (which follows the builder pattern), you'll see that it is full of Use* like methods, which allow the programmer to configure this behavior:

Configuration and Startup settings

In the preceding example, the Kestrel web server is used, but other web servers can be specified. The code also indicates that you use the current directory as the content root to integrate with IIS (that's why this is optional) and to use the Startup instance available in order to finish its configuration before actually building the server.

Once built, the server is launched, and that's the reason for the information we see in the console if we select self-hosting instead of IIS.

Self-hosted applications

Self-hosted applications have a number of benefits, as we said: the application carries out anything it needs to run. That is, there's no need to have .NET Core preinstalled, which makes this option pretty useful for constrained environments.

Operationally, it works like a normal native executable, and we can build it for any of the supported platforms. Future plans are to convert this executable into pure native executables depending on the platform to be used.

Before digging into MVC, if you want to serve a static file, you'll have to ensure that the UseContentRoot method has been configured, and you have to add another piece of middleware indicating that. Just add the following to your Configure method and add some static content that you can invoke:

app.UseStaticFiles();

In my case, I've created a very simple index.html file with a couple of HTML text tags and an img tag to make a dynamic call to the http://lorempixel.com site in order to serve an image file of size 200 x 100:

<h2>ASP.NET Core 1.0 Demo</h2>
<h4>This content is static</h4>
<imgsrc="http://lorempixel.com/200/100"alt="Random Image"/>

If you leave this file in the wwwroot directory, you can now invoke the http://localhost:<port>/index.html address, and you should see the page just as well:

Self-hosted applications

Consequently, nothing prevents you from using ASP.NET Core technologies to build and deploy static sites or even sites that perform a functionality depending on the input without the need to use MVC, Web Pages, Web Forms, or other classic ASP.NET elements.

Once we understand the basic structure of ASP.NET Core, it's time to look at a more complex project, (MVC type), similar to the typical initial solution that Microsoft used to include in previous templates, including controllers, views, use of Razor, and third-party resources, such as BootStrap and jQuery, among others.

But before we get into that, let me just indicate some surprising results obtained recently in benchmarks published by the ASP.NET Core development team: the performance gains using ASP.NET Core are meaningful.

The benchmark was made to compare classic ASP.NET 4.6, Node.js, ASP.NET Core (Weblist), ASP.NET Core on Mono, ASP.NET Core (CLR), ASP.NET Core (on Linux), and ASP.NET Core (Windows), resulting in the last case 1,150,000 requests per second in fine-grained requests (highly superior to Node.js). Refer to the following figure:

Self-hosted applications

ASP.NET Core 1.0 MVC

If we opt for a complete template when creating a new ASP.NET Core application, we'll find some meaningful changes and extended functionality.

I think it's interesting to compare both approaches in order to see exactly which elements are added or modified to permit these type of applications. First, pay attention to the new file structure.

Now, we recognize the typical elements that we already know from ASP.NET MVC applications: Controllers, Views, (no Model folder in this case because there's no need for it in the basic template), and another four folders with static resources pending from wwwroot.

They contain the recommended location folders for the CSS used in the application, static images, JavaScript files (for instance, to access the new ECMA Script2015 APIs), plus versions 3.3.6 of Bootstrap, version 2.2 of jQuery, and version 1.14 of the jQuery Validation plugin (of course, the version number will vary with time).

These files are loaded into the project via Bower. Under the dependencies section, you'll find a Bower folder that you can use—even dynamically—to change versions, update to higher ones, and so on.

Tip

If you right-click on any of the Bower entries, a contextual menu will offer to update the package, uninstall it, or manage other packages so that you can add new missing packages.

All this is under the wwwroot section. But taking a look at the Controllers and Views folders, you'll discover a—somehow—familiar structure and content:

ASP.NET Core 1.0 MVC

Of course, if you execute the application, the main page launches, similar to the previous versions of ASP.NET MVC—only, the structure has changed. Let's see how, starting with a review of the Startup.cs and Program.cs files.

The first thing to notice in the Startup content is that now, the class has a constructor. This constructor uses an object of type IConfigurationRoot, named Configuration, defined as public; so, whatever it contains is accessible all over the application.

As the documentation states:

"Configuration is just a collection of sources, which provide the ability to read and write name/value pairs. If a name/value pair is written to Configuration, it is not persisted. This means that the written value will be lost when the sources are read again."

For the project to work properly, you must configure at least one source. Actually, the current implementation does something else:

public Startup(IHostingEnvironmentenv)
{
  var builder = newConfigurationBuilder()
  .SetBasePath(env.ContentRootPath)
  .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
  .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
  .AddEnvironmentVariables();
  Configuration = builder.Build();
}

The process goes in two phases. First, a Configuration Builder object is created and configured to read from distinct sources (JSON files). In this manner, when the runtime creates the Startup instance, all the required values are already read, as shown in the next screenshot:

ASP.NET Core 1.0 MVC

The next important change from the original demo is that MVC is an optional service that requires to be explicitly registered. This is done in the ConfigureServices method.

Finally, the runtime calls Configure in the Startup object. This time, we see that Add Debug()is called, and depending on the application's environment (development or production), distinct error pages are configured. (By the way, also note the call to Add StaticFiles(), which we added in the previous demo.)

The last step in this middleware configuration process is to configure routes. Those of you who are experienced and who already know ASP.NET MVC will easily recognize the similar code structure that we used in the classic version, although here, the default configuration has been simplified.

This also explains why Configure Services should be called prior to Configure because it's using the MVC service this latter call adds.

With all this, the application is ready to start; so, the runtime goes to the entry point (the Main method in Program.cs).

Another interesting behavior is shown here: the web host is built. And WebHost Builder is in charge of it. Only when this builder is instantiated and configured does the process end, calling the Build()method. This method generates a working and tuned server, which is finally launched. A look at the code also tells us more about the structure:

public static void Main(string[] args)
{
  var host = new WebHostBuilder()
  .Use Kestrel()
  .Use ContentRoot(Directory.GetCurrentDirectory())
  .Use IISIntegration()
  .Use Startup<Startup>()
  .Build();

  host.Run();
}

Note how the UseStartup method connects the main program with the previously defined Startup object.

Naturally, if you want to check the properties of the final, running server, a breakpoint in the host.Run()call will inform you about that in the Services and Server Features properties.

Of course, there is much more about the runtime and the classes it uses to configure and execute the server, which you'll find in the documentation, and that goes far beyond the scope of this introduction.

As for the rest of the code (the business logic), it's pretty similar to what we had in classic MVC, but we'll find many additions and modifications in order to make the architecture cross-platform, besides certain native support for common developer tools, such as Bower, NPM, Gulp, Grunt, and so on.

A look at the HomeController class shows basically the same structure, with the exception that now the action methods are defined as being of type IActionResult instead of ActionResult:

public IActionResult About()
{
  ViewData["Message"] = "Your application description page.";

  return View();
}

So, we can add another action method by following exactly the same pattern. This happens to the Models section (not present here). A model should be defined as a POCO (Plain Old CLR Object) class, with little or no behavior. In this way, business logic is encapsulated and can be accessed wherever it's needed in the app.

Let's create a Model and an Action method and its corresponding view so that we can see how similar it is with respect to the previous version.

We'll create a new Model folder, and inside it, we'll add a class named PACKTAddress, where we'll define a few properties:

public classPACKTAddress
{
  public string Company { get; set; }
  public string Street { get; set; }
  public string City { get; set; }
  public string Country { get; set; }
}

Once compiled, we can create a new action method inside HomeController. We need to create an instance of the PACKTAddress class, fill its properties with the required information, and pass it to the corresponding view, which will receive and present the data:

public IActionResult PACKTContact()
{
  ViewData["Message"] = "PACKT Company Data";

  var viewModel = new Models.PACKTAddress()
  {
    Company = "Packt Publishing Limited",
    Street = "2nd Floor, Livery Place, 35 Livery Street",
    City = "Birmingham",
    Country = "UK"
  };
  return View(viewModel);
}

With this, the business logic for our new view is almost ready. The next step is to add a new view file of the same name as the action method, that will sit next to its siblings in the Views/Home folder.

In the view, we need to add a reference to the model we just passed and later use Tag Helpers in order to recover the data, presenting its results in the page.

This is quite easy and straightforward:

@model WebApplication1.Models.PACKTAddress
<h2>PACKT Publishing office information</h2>
<address>
  @Model.Company<br/>
  @Model.Street<br/>
  @Model.City, @Model.Country <br/>
  <abbrtitle="Phone">P:</abbr>
  0121 265 6484
</address>

A few things should be noticed when building this view. First, we have plain Intellisense in the view's editor, just like we did with the classic MVC. This is important so that we can always make sure that the context recognizes value models appropriately.

Thus, if we have compiled the code and everything is correct, we should see these helping features as we proceed with the creation of the view:

ASP.NET Core 1.0 MVC

Finally, we have to integrate our new view with the main page (_Layout.cshtml) by including a new menu entry to point to the view in the same way as the previous entries. So, the modified menu will be as follows:

<ulclass="nav navbar-nav">
  <li><aasp-controller="Home"asp-action="Index">Home</a></li>
  <li><aasp-controller="Home"asp-action="About">About</a></li>
  <li><aasp-controller="Home"asp-action="Contact">Contact</a></li>
  <li><aasp-controller="Home"asp-action="PACKTContact">PACKT Information</a></li>
</ul>

Here, you'll notice the presence of new customized attributes related to ASP.NET: asp-controller, asp-action, and so on. This is similar to the way we work with controllers when building AngularJS applications.

Also, note that we pass some extra information using the ViewData object, which has been recovered for preferable use instead of the previous ViewBag object.

Finally, I've added a link to this book's cover in a standard image (no problems or configuration features for that). When we launch the application, a new menu element will appear, and if we go to that link, we should see the new page inside the main application page, just like we expected:

ASP.NET Core 1.0 MVC

Managing scripts

As you've probably seen after a review of the folder's contents, there are more .json files related to configuration options. Actually, in this project, we see several files, each one in charge of some part of the configuration. Their purpose is the following:

  • launch Settings.json: It is located under Properties. It configures ports, browsers, basic URLs, and environment variables.
  • app Settings.json: It is located at root level, and not wwwroot. It defines logging values and is also the place to locate other application-related data, such as connection strings.
  • bower.json: It is located at root level, and not wwwroot). It defines which external components have to be updated in the application, utilizing Bower services: Bootstrap, jQuery, and so on.
  • bundle Config.json: It is located at root level, and not wwwroot. This is where you define which files are to be bundled and minified, indicating the original and final filenames in each case.

So, we've seen how the programming model has been improved, and there's much more to deal with in relation to the new Tag Helpers, other improvements in the modeling, data access, and so many other features that we cannot cover here, but I hope this has served as an introduction to the new architecture.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.232.189