Creating ASP.NET Core 2.2 Application Step-by-Step

This article will walk you through the creation of ASP.NET Core application using ASP.NET Core 2.2 from scratch, by utilizing the capabilities of package manager, EF Core, Identity API, Razor Class Library, etc.

Introduction

 
Rather than being more theoretical, this article will focus mainly on the implementation part. Wherever required, I’ll throw some light on the conceptual part too.
 

Prerequisite

 
Good knowledge of C# and working knowledge of web designing and its concepts.
 

Machine Configuration

 
Machine configuration used for this walkthrough is Visual Studio 2017 with .NET Core development tools/SDK. Make sure that you have at least .NET Core 2.2 installed.
 

Expectation

 
By the end of this article, the reader will have a working model of an ASP.NET Core website for a Cookie Store.
 

Good to Start

 
Background is all set and we are good to go. Let's start by creating our application in a step-by-step fashion.
 

Creating a New ASP.NET Core 2.2 Application

 
Let’s go to the Visual Studio. Go to File menu, select New and then select Project. On selecting the Web as a category, you will land upon the below dialog,
 
Creating ASP.NET Core 2.2 Application
 
Once you clicked on OK, you will witness the below dialog wherein you can re-verify the selected ASP.NET Core version. Being a learner, it’s good to go with an Empty template as we can take care of each and everything by ourselves rather than relying on auto generated code. Select Empty and proceed.
 
Creating ASP.NET Core 2.2 Application
 
Once the project is successfully created, you can see the dependency node is having required references added as shown below,
 
Creating ASP.NET Core 2.2 Application
 
Here, let’s remove appsettings.json file as we will be creating our own at a later point of time and add a new folder named wwwroot under the project. Once the wwwroot folder is created, you can notice that the folder icon is changed. This is a special folder which stores all the static files (css, image files, JavaScript files, etc.) and is mapped directly to the web site URL.
 

Plugging Middleware and Services

 
Let’s start with Startup class. This class holds two very important methods named ConfigureServices and Configure. Both these methods are automatically called by ASP.NET Core. Using ConfigureServices, we can add services to dependency injection container using IServiceCollection. So, let’s go and add MVC service as shown below,
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.    services.AddMvc();  
  4. }  
Next is ConfigureServices method, which is used to configure middleware pipeline with the components which will serve our requests.
 
Here is the initial code to plug the required middleware,
  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env)  
  2. {  
  3.  app.UseDeveloperExceptionPage();  
  4.  app.UseStatusCodePages();  
  5.  app.UseStaticFiles();  
  6.  app.UseMvcWithDefaultRoute();  
  7. }  
Please note that the sequence of adding middleware matters a lot. Now we have done the ground work for setting up the foundation for our application, let’s go ahead and setup other useful components.
 

Creating Model and Repository Classes

 
Let’s start by creating a Models folder under solution with a class named Cookie underneath it.
  1. public class Cookie  
  2. {  
  3.     public int Id {get;set; }  
  4.     public string Name { getset; }  
  5.     public string TinyDescription getset; } 
  6.     public string FullDescription { get; set; }  
  7.     public decimal Price { getset; }  
  8.     public string ImageUrl getset; }  
  9.     public bool IsCookieOfTheDay { getset; }  
  10. }  
Now quickly create an interface with name IRepository under the same folder with two initial methods as shown below,
  1. public interface IRepository  
  2. {  
  3.     IEnumerable<cookie> GetAllCookies();  
  4.     Cookie GetCookieById(int id);  
  5. }  
The next task would be to create some dummy data, which we can use to verify whether our application is working as expected or not.
 
Here is the implementation of IRepository interface,
  1. public class MockRepository : IRepository  
  2. {  
  3.     private List<cookie> _cookies;  
  4.     public MockRepository()  
  5.     {  
  6.         if (_cookies == null)  
  7.         {  
  8.             InitializeRepository();  
  9.         }  
  10.     }  
  11.   
  12.     private void InitializeRepository()  
  13.     {  
  14.         _cookies = new List<cookie>  
  15.         {  
  16.             new Cookie  
  17.             { Id=1,Name="Raspberry", Price=110,   
  18.               TinyDescription="Dark chocolate overloaded",                     
  19.                     FullDescription ="This is one of the best ever   
  20.                     soft and chewy cookie and also been awarded as the   
  21.                     best cookie several times.", IsCookieOfTheDay=false,  
  22.                     ImageUrl ="\\Images\\1.png"},  
  23.                 new Cookie{ Id=2, Name="Nuts Overloaded",   
  24.                 Price=100, TinyDescription="Truely healthy",  
  25.                     FullDescription ="This cookie is fully loaded   
  26.                     with nuts of various types and contains   
  27.                     nice amount of peanut butter.", IsCookieOfTheDay=true,  
  28.                     ImageUrl ="\\Images\\2.png"},  
  29.                 new Cookie{Id=3, Name="Chocolate",  
  30.                 Price=70,TinyDescription="Amazingly fruity",  
  31.                     FullDescription ="This cookie is best suited   
  32.                     for the chocolate lovers. It's less sweet and gives   
  33.                     very elegant taste.", IsCookieOfTheDay=false,  
  34.                     ImageUrl ="\\Images\\3.png"},  
  35.                 new Cookie{Id=4, Name="Delicious Oatmeal",Price=50,  
  36.                 TinyDescription="Truely healthy",  
  37.                     FullDescription ="This is one of the most moist and   
  38.                     flavourful cookie, which can make anyone's mood happy.",   
  39.                     IsCookieOfTheDay=false,  
  40.                     ImageUrl ="\\Images\\4.png"},  
  41.             };  
  42.         }  
  43.           
  44.     public Cookie GetCookie(int id)  
  45.     {  
  46.         return _cookies.FirstOrDefault(x => x.Id == id);  
  47.     }  
  48.   
  49.     public IEnumerable<cookie> GetCookies()  
  50.     {  
  51.         return _cookies;  
  52.     }  
  53. }  
Let’s go and register this IRepository with dependency injection container inside ConfigureServicesmethod as shown below:
 
services.AddTransient<irepository, mockrepository="">(); // get me new instance every time
 

Adding Controller

 
Let’s create a new folder named Controllers under project and add an empty controller with the name Home inside it. Briefly, controller is responsible for creating a response based on user’s request with the help of a model by calling a method. Such methods are generally known as action methods in terms of MVC. Below is the code to use IRepository inside controller to get the data from model,
  1. public class HomeController : Controller  
  2. {  
  3.     private readonly IRepository _repository;  
  4.     public HomeController(IRepository repository)  
  5.     {  
  6.         _repository = repository;  
  7.     }  
  8.   
  9.     public IActionResult Index()  
  10.     {  
  11.         return View();  
  12.     }  
  13. }  
 

Adding View

 
So far, we are already done with basic Model and Controller. So, the only thing pending is View. In ASP.NET Core 2.2, a View can be of two types – Regular/Plain view and Strongly-typed view. But in most of the scenarios, strongly-typed view is required. Here, we would be using Razor.
 
So, let’s create a new folder named Views under project along with a subfolder named as Home. Right-click on the Home folder and add new item Razor View. On successful addition of View, you will notice that a new file with name Index.cshtml is added under Home folder.
 
Now it’s time to verify whether view and controller are tied properly and able to communicate. For this verification purpose, let’s add some title to the page and we will pass the value of this title from controller class using ViewBag. Similarly, we will also show some information about the cookies. Below is the updated method of HomeController,
  1. public IActionResult Index()  
  2. {  
  3.     ViewBag.Title = "Cookies and only Cookies";  
  4.     var cookies = _repository.GetAllCookies().OrderByDescending(x=>x.Price);  
  5.     return View(cookies);  
  6. }  
Next is to update the view to read this title value. Here is the code for this,
 
Creating ASP.NET Core 2.2 Application
 
Now, if you will run the application, you would be able to see the below output,
 
Creating ASP.NET Core 2.2 Application
 

Improvising on Including HTML Markup

 
In the above snippet, we have seen that the complete HTML markup is written to display a page. Now what if we have lots of views? Are we going to repeat this HTML code for each and every page?
 
Certainly not. Here comes the templating. We can create a template and refer to that template in all the views. Doing this will also reduce the amount of code in individual views. It’s obvious that, if something is to be shared among many components, then it has to be kept in a shared location and that’s where shared folder is introduced as part of the MVC design.
 

Introducing Layout Template

 
Add a new folder under Views folder and name it as Shared. Right click on the Shared folder and add a new item Razor Layout. Default name of this newly added item is _Layout.cshtml and it looks like below,
 
Creating ASP.NET Core 2.2 Application
 
If you will look closely, you may notice that _Layout.cshtml and our view contains most of the common code. So, it’s time to integrate _Layout.cshtml into our view and after integration, our view will contain the below code only,
 
Creating ASP.NET Core 2.2 Application
 

Refining the View Even Further Using _ViewStart

 
We did a really good job by reducing the line of codes in our view file, but there is still a need to do more. Again, the layout, which we integrated in our view is going to be duplicated for each and every view. Can we get rid of this duplication also?
 
Of course, Yes. Let’s add another new item Razor View Start under Views folder with a default name as _ViewStart.cshtml. This file comes with the default code in it as shown below and is invoked automatically,
  1. @{  
  2.     Layout = "_Layout";  
Now you can see that this file has already plugged Layout for us, which means the line of code in our view is further reduced to just a few lines as shown below,
 
Creating ASP.NET Core 2.2 Application
 
You can re-run your application and verify that it is working as expected.
 

Introducing ViewModel

 
You must have noticed that our view is getting data from multiple paths. So, why can’t we get rid of this and create a single entity which would be the source for our view. Let’s try this.
 
We will add a new folder named ViewModels under project with a new class named HomeViewModel under it. This ViewModel class will be the data source for our Home view. As of now, we will take the bare minimum fields as shown below,
  1. public class HomeViewModel  
  2. {  
  3.     public string Title { getset; }  
  4.     public List<cookie> Cookies { getset; }  
  5. }  
Accordingly, we must update our Index method inside HomeController as shown below,
  1. public IActionResult Index()  
  2. {  
  3.     HomeViewModel viewModel = new HomeViewModel  
  4.     {  
  5.             Title = "Cookies and only Cookies",  
  6.             Cookies = _repository.GetAllCookies().OrderByDescending(x => x.Price).ToList()  
  7.     };  
  8.              
  9.     return View(viewModel);  
  10. }  
Lastly, let’s update the View with reference of HomeViewModel as shown below,
 
Creating ASP.NET Core 2.2 Application
 
Re-verify the output, it should still be the same.
 

Improving Look and Feel of the View using Bootstrap

 
This can be done using a client-side package known as Bootstrap. Depending on the version and updates of Visual Studio, one can choose any of the package manager like Bower, Library Manager (LibMan), etc. Here, I’ll go with the Library Manager as my Visual Studio version is 15.8.5. Let’s go and add new item ‘Add Client-Side Library’ at project level and furnish the details as shown below,
 
Creating ASP.NET Core 2.2 Application
 
Next is to add images to our application. For that, we have to create another folder named Images under wwwroot.
 
Then we have to add a style sheet to our app and to do that, a new folder named content has to be added under wwwroot and inside that, we have to create a css named site.css. Once done, we can add the below basic style,
  1. body {  
  2.     padding-top:50px;  
  3.     padding-bottom:20px;  
  4.     background-imageurl();  
  5.     background-repeatrepeat;  
  6. }  
  7.   
  8. .body-content{  
  9.     padding-left15px;  
  10.     padding-right15px;  
  11. }  
Finally, we have to associate css and bootstrap to our _Layout page using <Link> as shown below,
 
Creating ASP.NET Core 2.2 Application
 
Next is to update view file to accommodate bootstrap. Below is the complete code,
 
Creating ASP.NET Core 2.2 Application
 
If everything was done well, you will see the below web page on running the application.
 
Creating ASP.NET Core 2.2 Application
 
Next is to associate the real database with our application.
 

Getting Real Data using EF Core

 
EF core is an ORM which supports cross-platform capabilities like ASP.NET Core. It’s worth mentioning that as of now, EF Core only supports Code-First approach. Below are the steps which need to be taken care to fully integrate the EF with our application,
  • Create Entity Classes
  • Create Database Context
  • Setup Connection string
  • Changes to Application Configuration 
Let’s create a new class named DatabaseContext under Models folder and place the below code in it,
  1. public class DatabaseContext:DbContext  
  2. {  
  3.     public DatabaseContext(DbContextOptions<databasecontext> options):base(options)  
  4.     {  
  5.   
  6.     }  
  7.     public DbSet<cookie> Cookies { getset; }  
  8. }  
DataContext plays a vital role of connecting our application to the actual database. So far, we were using mocked values using MockRepository class. So, it’s time to create an actual class named Repository under Models folder having the below code,
  1. public class Repository:IRepository  
  2. {  
  3.     private readonly DatabaseContext _dbContext;  
  4.     public Repository(DatabaseContext databaseContext)  
  5.     {  
  6.         _dbContext = databaseContext;  
  7.     }  
  8.   
  9.     public Cookie GetCookie(int id)  
  10.     {  
  11.         return _dbContext.Cookies.FirstOrDefault(x => x.Id == id);  
  12.     }  
  13.   
  14.     public IEnumerable<cookie> GetCookies()  
  15.     {  
  16.         return _dbContext.Cookies;  
  17.     }  
  18. }  
Next is to set up the connection string. I hope most of you are aware that ASP.NET Core no longer uses Web.Configfile, rather it uses appsettings.json. So, add a new item ‘App Settings File’ named appsettings.json and update the connection string in it. My code looks something like this,
  1. {  
  2.   "ConnectionStrings": {  
  3.     "DefaultConnection":   
  4.          "Server=(localdb)\\MSSQLLocalDB;Database=CookiesDataStore;  
  5.           Trusted_Connection=True;MultipleActiveResultSets=true"  
  6.   }  
  7. }  
As a final step, we have to register EF core with our application and that can be done by updating ConfigureServices() in Startup class as shown below,
  1. public class Startup  
  2. {  
  3.     public IConfiguration Configuration { getset; }  
  4.   
  5.     public Startup(IConfiguration configuration)  
  6.     {  
  7.         Configuration = configuration;  
  8.     }  
  9.   
  10.     public void ConfigureServices(IServiceCollection services)  
  11.     {  
  12.         services.AddDbContext<DatabaseContext>(options => options.UseSqlServer  
  13.                      (Configuration.GetConnectionString("DefaultConnection")));  
  14.         services.AddTransient<IRepository, Repository>();  
  15.         services.AddMvc();  
  16.     }  
  17.   
  18.         // This method gets called by the runtime.   
  19.         // Use this method to configure the HTTP request pipeline.  
  20.     public void Configure(IApplicationBuilder app, IHostingEnvironment env)  
  21.     {  
  22.         app.UseDeveloperExceptionPage();  
  23.         app.UseStatusCodePages();  
  24.         app.UseStaticFiles();  
  25.         app.UseMvcWithDefaultRoute();  
  26.     }  
  27. }  
It’s time to build the code and verify that there are no more compilation errors.
 

Creating a Database

 
In order to view something on a web page, we need data in our database. Here, we will use Package Manager console with the below command:
 
PM> add-migration CookiesDatabaseMigration
PM> update-database
 
To add some initial data to database, we will create a new class called DbInitializer under Models folder with the below code,
  1. public static class DbInitializer  
  2. {  
  3.     public static void Seed(DatabaseContext dbContext)  
  4.     {  
  5.         if (!dbContext.Cookies.Any())  
  6.         {  
  7.             dbContext.AddRange(  
  8.                 new Cookie  
  9.                 {                          
  10.                     Name = "Choco Chips",  
  11.                     Price = 80,  
  12.                     TinyDescription = "Dark chocolate overloaded",  
  13.                     FullDescription = "This is one of the most moist   
  14.                                        and flavourful cookie,   
  15.                                        which can make anyone's mood happy.",  
  16.                     IsCookieOfTheDay = false,  
  17.                     ImageUrl = "\\Images\\Chocochip.png"  
  18.                 },  
  19.                 new Cookie  
  20.                 {  
  21.                     Name = "Nuts & Peanuts",  
  22.                     Price = 75,  
  23.                     TinyDescription = "Truely healthy",  
  24.                     FullDescription = "This cookie is fully loaded   
  25.                                        with nuts of various types and   
  26.                                        contain nice amount of peanut butter.",  
  27.                     IsCookieOfTheDay = true,  
  28.                     ImageUrl = "\\Images\\ChocolateChipWalnut.png"  
  29.                 },  
  30.                 new Cookie  
  31.                 {  
  32.                     Name = "Berries & Rasins",  
  33.                     Price = 50,  
  34.                     TinyDescription = "Amazingly fruity",  
  35.                     FullDescription = "This is one of the best ever soft   
  36.                                        and chewy cookie and also been awarded   
  37.                                        as the best cookie several times.",  
  38.                     IsCookieOfTheDay = false,  
  39.                     ImageUrl = "\\Images\\Nuts.png"  
  40.                 },  
  41.                 new Cookie  
  42.                 {  
  43.                     Name = "Coconut",  
  44.                     Price = 100,  
  45.                     TinyDescription = "Truely healthy",  
  46.                     FullDescription = "This cookie is best suited   
  47.                                        for the nut lovers. It's less sweet and   
  48.                                        gives very elegant taste.",  
  49.                     IsCookieOfTheDay = false,  
  50.                     ImageUrl = "\\Images\\Coconut.png"  
  51.                 }  
  52.                 );  
  53.         }  
  54.         dbContext.SaveChanges();  
  55.     }  
  56. }  
Next is to call this DbInitializer from Program class and here is the updated code for the same,
  1. public static void Main(string[] args)  
  2. {  
  3.     var host = CreateWebHostBuilder(args).Build();  
  4.     using (var scope = host.Services.CreateScope())  
  5.     {  
  6.         var services = scope.ServiceProvider;  
  7.         try  
  8.         {  
  9.             var context = services.GetRequiredService<databasecontext>();  
  10.             DbInitializer.Seed(context);  
  11.         }  
  12.         catch (Exception ex)  
  13.         {  
  14.             // TODO  
  15.         }  
  16.     }  
  17.   
  18.     host.Run();  
Now quickly run the application and you will see the same output as before.
 

Adding Navigation

 
As part of navigation, we will create a details page which will hold the details of the selected cookie. To get the details of a selected cookie, a new method has to be added in HomeController class as shown below,
  1. public IActionResult Details(int id)  
  2. {  
  3.     var cookie = _repository.GetCookie(id);  
  4.     return View(cookie);  
  5. }   
And of course, we have to add the view for this method with the name Details.cshtml under HomeController,
 
Creating ASP.NET Core 2.2 Application
 
Now, we want to navigate to Details view from Index view. So, we will use tag helpers as shown below,
 
Creating ASP.NET Core 2.2 Application
 
One last thing is to add navigation on our front page and that can be done using nav element. Below is the updated code of _Layout.cshtml,
 
Creating ASP.NET Core 2.2 Application
 
Now quickly run the application and you will be able to see the links on top of the page as,
 
Creating ASP.NET Core 2.2 Application
 

Adding Form to Request Comments

 
Now, let’s add a form by which the user can give feedback or comments for our lovely cookies. In order to do that, we have to add a new model entity named Feedback as shown below,
  1. public class Feedback  
  2. {  
  3.     public int Id { getset; }  
  4.     public string Name { getset; }  
  5.     public string Content { getset; }  
  6.     public string Email { getset; }  
  7. }  
Next is to update database with this new entity and that can be done by running add-migration command as
 
PM> Add-Migration Feedback
 
Let’s quickly add the interface and a method in that to save feedback and code for that is as shown below,
  1. public class FeedbackRepository: IFeedbackRepository  
  2. {  
  3.     private readonly DatabaseContext _dbContext;  
  4.     public FeedbackRepository(DatabaseContext context)  
  5.     {  
  6.         _dbContext = context;  
  7.     }  
  8.     public void AddFeedback(Feedback feedback)  
  9.     {  
  10.         _dbContext.Feedbacks.Add(feedback);  
  11.         _dbContext.SaveChanges();  
  12.     }  
  13. }  
Next is to register this new interface with dependency injection container under ConfigureServices method,
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     ...  
  4.     services.AddTransient<IFeedbackRepository, FeedbackRepository>();  
  5.     services.AddMvc();  
  6. }  
Now it’s time to make UI changes for feedback functionality. Below is the code for FeedbackController and its view,
  1. public class FeedbackController : Controller  
  2. {  
  3.     private readonly IFeedbackRepository _feedbackRepository;  
  4.     public FeedbackController(IFeedbackRepository feedbackRepository)  
  5.     {  
  6.         _feedbackRepository = feedbackRepository;  
  7.     }  
  8.   
  9.     public IActionResult Index()  
  10.     {  
  11.         return View();  
  12.     }  
  13.   
  14.     [HttpPost]  
  15.     public IActionResult Index(Feedback feedback)  
  16.     {  
  17.         _feedbackRepository.AddFeedback(feedback);  
  18.         return View();  
  19.     }  
  20. }  
Creating ASP.NET Core 2.2 Application
 
Next is to add the Feedback link on our application front page and that can be done by updating Layout page as shown below,
 
Creating ASP.NET Core 2.2 Application
 
On click of Feedback, navigation will occur on the below page,
 
Creating ASP.NET Core 2.2 Application
 

Securing Application Configuring Identity

 
As part of this section, we will explore the ASP.NET Core identity API and to accommodate this, we have to update a bit of our existing code. Let’s start with DatabaseContext class. Now, instead of inheriting with DbContext, we will inherit DatabaseContext class with IdentityDbContext<IdentityUser> and at the same time, we have to update our middleware pipeline by adding app.UseAuthentication().
Next, we have to update our database with user information. So, we have to run add and update migration as shown below:
 
PM> add-migration AuthenticationAdded 
PM> update-database
 
On successful execution, you will see that the below tables are created in the database,
 
Creating ASP.NET Core 2.2 Application
 

Adding Authentication Support

 
To add authentication capabilities to our application, we will use out of box functionality provided by the Razor Class Library which comes with ASP.NET Core. To do this, right click on the project and select Add and then New Scaffolded Item…, select Identity.
 
After the above action, we will get the below dialog which mentions all the views that are readily available for us to use. Here, I will be choosing three views with required data context as shown below,
 
Creating ASP.NET Core 2.2 Application
  1. public class IdentityHostingStartup : IHostingStartup  
  2.     {  
  3.         public void Configure(IWebHostBuilder builder)  
  4.         {  
  5.             builder.ConfigureServices((context, services) => {  
  6.                 services.AddDefaultIdentity<identityuser>(IdentityUser)  
  7.                 .AddEntityFrameworkStores<databasecontext>(DatabaseContext);  
  8.             });  
  9.         }  
  10.     }  
By the above code, we are using our database context to save identity information. Finally, we have to provide a link on our navigation bar, so just plug the login stuff to _Layout page by going with <Partial> as shown below,
 
Creating ASP.NET Core 2.2 Application
 
Now run the application and you will see two additional links added to the navigation bar. Of course, you can go ahead and play with Register and Login functionality now. Isn’t it cool?

 
Adding Authorization Support

 
Up to now, we have provided login functionality but what about restricting any user from using some functionality of a website, for example, only the logged in users should be allowed to provide feedback. Here comes in the concept of Authorization. Let’s quickly do it by decorating our Feedback controller with Authorize attribute as shown below,
  1. [Authorize]  
  2. public class FeedbackController : Controller  
  3. {  
  4.    ……           
  5. }  
We are done and it’s time to run the application and verify all the things.
 
Hope you enjoyed creating your own ASP.NET Core application with all the basic concepts covered.
 

References

 
Course by Gill Cleeren and MSDN docs