Introduction
This article will walk you through the basic steps on how to create a simple web application using MVC in ASP.NET Core.
Background
If you stumble into this article then I assume you are already familiar about how the old MVC works. I will not be covering the details about MVC itself here so if you are new to ASP.NET MVC then you might want to look at my series of articles below:
You can also download my EBook: ASP.NET MVC 5: A Beginner’s Guide Book
Using the code
Before we get our hands dirty, let's talk about a bit of MVC.
ASP.NET Core the Unified Framework
Figure 1: ASP.NET Core
ASP.NET Core will see MVC, Web API and probably Web Pages combined into one framework called ASP.NET Core 1.0, previously named as ASP.NET 5 MVC 6.
In previous versions of ASP.NET MVC, MVC controllers were different from Web API controllers. An MVC controller used the System.Web.MVC.Controller base class and a Web API controller used the System.Web.Http.ApiController base class. In MVC Core, there is only one Controller base class for both MVC and Web API controllers that is the Microsoft.AspNetCore.Mvc.Controller class.
The merge is also true for HTML helpers in both MVC and Web Pages that are implemented differently before. The Web Pages programming model isn't available yet for the current release so we can never really tell what will be the other features that they're going to merge, but we can assume that the traditional MVC model-binding will be available to it.
Now that you already have an idea of what is ASP.NET Core all about and how it differs from the previous versions of ASP.NET MVC we can now go ahead and start digging in. Keep in mind that for this specific demo I'm only covering the MVC stuff.
Let's Begin!
Let's go ahead and fire up Visual Studio 2015 and select File > New > Project. In the New Project dialog select Templates > Visual C# > ASP.NET Core Web Application. See the following figure below for a clear view:
Figure 2: New Project Dialog
Name your project to whatever you like and then click OK. For the simplicity of this demo I named the project as “MVCCoreDemo”. Now after that you should be able to see the “New ASP.NET Core Project” dialog:
Figure 3: Project Templates
The new ASP.NET Core project has three default templates: Empty, Web API and Web Application.
The goal here is to build an MVC Core application from scratch so select ASP.NET Core Empty template from the dialog above. Then click OK to let Visual Studio generate the necessary files and templates needed for you.
You should be able to see something as in the following image:
Figure 4: New Solution Project
If you have worked with previous versions of ASP.NET before then you will notice that the new project structure is totally different. The project now includes these files:
- src folder: contains all projects that contain source code that make up your application.
- Program.cs: this file contains the Main method of an ASP.NET Core RC2 app, which is responsible for configuring and running the app.
- global.json: this is where you put solution-level settings and allows you to do project-to-project references.
- wwwroot: is a folder in which all your static files will be placed. These are the assets that the web app will serve directly to the clients, including HTML, CSS, Image and JavaScript files.
- project.json: contains project settings.
- Startup.cs: this is where you put your startup and configuration code.
- References: it contains the .NETCoreApp Version 1 runtime references.
For details about the new features in ASP.NET Core check out this article: ASP.NET Core: The New ASP.NET in Town!
Setting up the MVC folder structures
To follow the MVC standard pattern and the separation of concerns, let's create the “Models”, “Views” and “Controllers” folders just like in the following image:
Figure 5: M V C folders
Introducing the project.json file
The “project.json” file serves as the new project file (.csproj/.vbproj). This is where we put all the dependencies that our application requires.
The cool thing about the project.json file is that it provides intellisense for the available packages when you add or edit dependencies. All packages that you've added from this file will be automatically pulled from NuGet. Correspondingly when you remove packages it automatically removes them from your project reference. That's pretty awesome!
Up to this point there's really no MVC in our app yet since we are creating an empty ASP.NET Core template. To add the MVC dependency to our project just modify the project.json file by adding “Microsoft.AspNetCore.MVC” under the dependencies section. We also need to add the "Microsoft.AspNetCore.Diagnostics" so we can troubleshoot our App when we encounter any issues. Your project.json should look like this:
{
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0-rc2-3002702",
"type": "platform"
},
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final"
},
"tools": {
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
"version": "1.0.0-preview1-final",
"imports": "portable-net45+win8+dnxcore50"
}
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"dotnet5.6",
"dnxcore50",
"portable-net45+win8"
]
}
},
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"runtimeOptions": {
"gcServer": true
},
"publishOptions": {
"include": [
"wwwroot",
"web.config"
]
},
"scripts": {
"postpublish": [ "dotnet publish-iis
--publish-folder %publish:OutputPath%
--framework %publish:FullTargetFramework%" ]
}
}
As of this writing, MVC 1.0.0-rc2-final is the latest version. Now save the project.json file to restore the required NuGet packages. The MVC framework should now be added to our application as shown in the figure below:
Figure 6: Restoring NuGet Package
Configure the Application Pipeline
Since we are done with adding the MVC dependency, the next step is to add the MVC framework in the pipeline. So open up Startup.cs file and add the following code under the Configure method:
public void Configure(IApplicationBuilder app){
app.UseDeveloperExceptionPage();
app.UseMvc(m => {
m.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action="Index"});
});
}
The code above is much the same as the old MVC for defining routes, except that we are now using the UseMvc method to setup the routes. You may also want to look at Attribute Routing in MVC Core.
We're not done yet.
Adding the MVC in the pipeline doesn't mean that we are now good to go. We still need to hook up the pieces of MVC by adding the dependencies that MVC Core requires. So your ConfigureServices method should now look like this:
public void ConfigureServices(IServiceCollection services){
services.AddMvc();
}
The AddMvc() is an extension method that does all the magic for you. It basically adds the dependencies needed for MVC Core.
Adding Models
Now let's add a simple model that we can test on. For this example I'm creating the following class under the “Models” folder:
namespace MVCCoreDemo.Models
{
public class DOTAHero
{
public int ID { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
}
And then added the following class that manages the “DOTAHero” class:
using System.Collections.Generic;
using System.Linq;
namespace MVCCoreDemo.Models
{
public class HeroManager
{
readonly List<DOTAHero> _heroes = new List<DOTAHero>() {
new DOTAHero { ID = 1, Name = "Bristleback", Type="Strength"},
new DOTAHero { ID = 2, Name ="Abbadon", Type="Strength"},
new DOTAHero { ID = 3, Name ="Spectre", Type="Agility"},
new DOTAHero { ID = 4, Name ="Juggernaut", Type="Agility"},
new DOTAHero { ID = 5, Name ="Lion", Type="Intelligence"},
new DOTAHero { ID = 6, Name ="Zues", Type="Intelligence"},
new DOTAHero { ID = 7, Name ="Trent", Type="Strength"},
new DOTAHero { ID = 8, Name ="Axe", Type="Strength"},
new DOTAHero { ID = 9, Name ="Bounty Hunter", Type="Agility"},
new DOTAHero { ID = 10, Name ="Chaos Knight", Type="Strength"}
};
public IEnumerable<DOTAHero> GetAll { get { return _heroes; } }
public List<DOTAHero> GetHeroesByType(string type)
{
return _heroes.Where(o => o.Type.ToLower().Equals(type.ToLower())).ToList();
}
}
}
The “HeroManager” class contains a readonly property that contains a list of heroes. For simplicity, the data is obviously static. In a real scenario you may need to get the data from a storage medium such as database or any files that stores your data. It also contains a GetAll property that returns all the heroes, and finally a GetHeroesByType() method that returns a list of heroes based on the hero type.
Adding a Controller
Adding a controller is pretty much the same as adding controllers in the old MVC. Just right-click on the “Controllers” folder and then select Add > New Item. In the dialog select MVC Controller Class as shown in the following figure:
Figure 7: Adding a Controller
Then just go ahead and click Add to generate the controller class for you. Here's the HomeController class:
using Microsoft.AspNetCore.Mvc;
using MVCCoreDemo.Models;
namespace MVCCoreDemo.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
HeroManager HM = new HeroManager();
var heroes = HM.GetAll;
return View(heroes);
}
}
}
There's nothing really fancy in the code above. It's just a controller that contains an IActionResult() method that returns the list of heroes that is coming from the model.
Adding a View
Right-clicking on the controller's action name doesn't bring up the “Add New Item” dialog. So to add views just do the same thing as what we did for adding the controller. But before that we must add a “Home” folder under the “Views” folder to follow the MVC convention. Right-click on the “Home” folder and select Add > New Item > MVC View Page:
Figure 8: Adding a View
Click add to generate the file. Now there's no magic in creating our view since we didn't use any scaffolding template. Instead we need to setup the view by hand. Here's how my view looks like:
@model IEnumerable<MVCCoreDemo.Models.DOTAHero>
<h3>My Favorite DOTA 2 Heroes</h3>
<ul>
@foreach (var p in Model)
{
<li>@($"{p.Name} {p.Type}")</li>
}
</ul>
The view above is a strongly-typed view. By including a @model statement at the top of the view template file, you can specify the type of object that view expects. In this case it uses the IEnumerable<MVCCoreDemo.Models.DOTAHero>.
Output
The following is the simple output when running the app in the browser.
Figure 9: Output
That's it! Now let's try to look at the other new cool features in MVC Core.
Introducing Inject
ASP.NET MVC Core has a few new directives that we can use in our application. Here we'll have a look at how to use @inject. The @inject directive allows you to inject some method calls from a class or service directly into your view. To see that in action, I've created a new class called “HeroStats” under the Models folder. Here's the simple class that exposes some async methods:
using System.Linq;
using System.Threading.Tasks;
namespace MVCCoreDemo.Models
{
public class HeroStats
{
private HeroManager _manager = new HeroManager();
public async Task<int> GetHeroCount()
{
return await Task.FromResult(_manager.GetAll.Count());
}
public async Task<int> GetHeroCountByType(string type)
{
return await Task.FromResult(_manager.GetHeroesByType(type).Count);
}
}
}
The class above initializes a new instance of HeroManager object. It also contains two main async methods. GetHeroCount() returns the total count of the heroes and GetHeroCountByType() returns the number of heroes based on a given type. Quite simple and there is nothing much in that class. Here's how to inject the class into the View:
@model IEnumerable<MVCCoreDemo.Models.DOTAHero>
@inject MVCCoreDemo.Models.HeroStats Stats
<h3>My Favorite DOTA 2 Heroes</h3>
<ul>
@foreach (var p in Model)
{
<li>@($"{p.Name} {p.Type}")</li>
}
</ul>
<div>
<h4>Stats</h4>
<p>Number of Strength Heroes: @await Stats.GetHeroCountByType("strength")</p>
<p>Number of Agility Heroes: @await Stats.GetHeroCountByType("agility")</p>
<p>Number of Intelligence Heroes: @await Stats.GetHeroCountByType("intelligence")</p>
<p>Total Heroes Heroes: @await Stats.GetHeroCount()</p>
</div>
Now in order for it to work we need to configure the AddTransient() method by adding the HeroStats model into it. So the Configure method would now look like this:
public void ConfigureServices(IServiceCollection services){
services.AddMvc();
services.AddTransient<MVCCoreDemo.Models.HeroStats>();
}
Running the app will display the following output:
Figure 10: Output
Introducing View Components
Another cool feature in MVC Core is the “View Components”. If you remember, in previous versions of ASP.NET MVC, the Html.Action() helper is typically used to invoke a sub-controller. A sub-controller may display stuff like tag clouds, dynamic links, side bars or whatever. ASP.NET MVC Core introduced the new View Component to replace widgets that use Html.Action().
View Components also supports fully async allowing you to make a view component asynchronous.
Now let's try to create a very simple view component and let's see how they are used in MVC. To start, create a new folder at the root of your application and name it “ViewComponents”. Within that folder create a new class and name it “HeroListViewComponent” and copy the following code:
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MVCCoreDemo.Models;
namespace MVCCoreDemo.ViewComponents
{
public class HeroListViewComponent: ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string type){
var heroes = await GetHeroesAsync(type);
return View(heroes);
}
private Task<IEnumerable<DOTAHero>> GetHeroesAsync(string type){
return Task.FromResult(GetHeroes(type));
}
private IEnumerable<DOTAHero> GetHeroes(string type){
HeroManager HM = new HeroManager();
return HM.GetHeroesByType(type);
}
}
}
Just like the controllers, view components also follow a convention for naming classes. This means that you can create a view component by adding the suffix “ViewComponent” to your class. Adding to that VCs must be public, non-nested and non-abstract classes.
Notes
You can also use the [ViewComponent] attribute in your class when referencing a ViewComponent.
You can use the overload method of the View() to specify a view to render from your InvokeAsync method. For example return View(“YourViewName”,model).
The InvokeAsync exposes a method that can be called from a view and it can take an arbitrary number of arguments. As you have seen from the code above, we passed in the parameter “type” in the method to filter the data.
Now let's add the view for the View Component that we just have created. Keep in mind that VC follows a convention too when referencing views. So the first thing to do is to create a new folder within the Home folder. The folder name must be “Components”. Since we need to follow a convention, the next thing to do is to create another new folder within the Components folder, this time the folder name must match your class name minus the “ViewComponents” suffix. In this case name the folder “HeroList”. Finally add a new view within the HeroList folder and name it “Default” since we didn't specify the view to render in our InvokeAsync code. Your project structure should now look like the following:
Figure 11: Solution Explorer
Now in your Default.cshtml file, add the following markup for your View Component's view:
@model IEnumerable<MVCCoreDemo.Models.DOTAHero>
<h3>@Model.First().Type Heroes</h3>
<ul>
@foreach (var p in Model)
{
<li>@p.Name</li>
}
</ul>
And here's how we call the View Component from the main view (Index.cshmtl):
<div>
@await Component.InvokeAsync("HeroList", new { type = "agility" })
</div>
Running the page will produce the following output:
Figure 12: Final Output
That's simple! I hope you will find this article useful. Stay tuned for more! :)
Summary
In this article, we've learned to create a simple MVC Core app from the scratch. We also explored few of the cool features in MVC Core.