Introduction
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. So, 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 would be having a working model of 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:
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 every thing by ourselves rather than relying on auto generated code. Select Empty and proceed.
Once the project is successfully created, you can see the dependency node is having required references added as shown below:
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 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:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
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:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
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.
public class Cookie
{
public int Id { get; set; }
public string Name { get; set; }
public string TinyDescription { get; set; }
public string FullDescription { get; set; }
public decimal Price { get; set; }
public string ImageUrl { get; set; }
public string ImageThumbnailUrl { get; set; }
public bool IsCookieOfTheDay { get; set; }
}
Now quickly create an interface with name IRepository
under the same folder with two initial methods as shown below:
public interface IRepository
{
IEnumerable<cookie> GetAllCookies();
Cookie GetCookieById(int id);
}
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:
public class MockRepository : IRepository
{
private List<cookie> _cookies;
public MockRepository()
{
if (_cookies == null)
{
InitializeRepository();
}
}
private void InitializeRepository()
{
_cookies = new List<cookie>
{
new Cookie
{ Id=1,Name="Raspberry", Price=110,
TinyDescription="Dark chocolate overloaded",
FullDescription ="This is one of the best ever
soft and chewy cookie and also been awarded as the
best cookie several times.", IsCookieOfTheDay=false,
ImageUrl ="\\Images\\1.png"},
new Cookie{ Id=2, Name="Nuts Overloaded",
Price=100, TinyDescription="Truely healthy",
FullDescription ="This cookie is fully loaded
with nuts of various types and contains
nice amount of peanut butter.", IsCookieOfTheDay=true,
ImageUrl ="\\Images\\2.png"},
new Cookie{Id=3, Name="Chocolate",
Price=70,TinyDescription="Amazingly fruity",
FullDescription ="This cookie is best suited
for the chocolate lovers. It's less sweet and gives
very elegant taste.", IsCookieOfTheDay=false,
ImageUrl ="\\Images\\3.png"},
new Cookie{Id=4, Name="Delicious Oatmeal",Price=50,
TinyDescription="Truely healthy",
FullDescription ="This is one of the most moist and
flavourful cookie, which can make anyone's mood happy.",
IsCookieOfTheDay=false,
ImageUrl ="\\Images\\4.png"},
};
}
public Cookie GetCookie(int id)
{
return _cookies.FirstOrDefault(x => x.Id == id);
}
public IEnumerable<cookie> GetCookies()
{
return _cookies;
}
}
Let’s go and register this IRepository
with dependency injection container inside ConfigureServices
method as shown below:
services.AddTransient<IRepository, MockRepository>();
Adding Controller
Let’s create a new folder named Controllers under project and add an empty controller with the name Home
inside it. Just to brief, 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.
public class HomeController : Controller
{
private readonly IRepository _repository;
public HomeController(IRepository repository)
{
_repository = repository;
}
public IActionResult Index()
{
return View();
}
}
Adding View
Till this point, we are already done with basic Model and Controller. So, the only thing pending is View. In ASP.NET Core 2.2, 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 sub folder 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
:
public IActionResult Index()
{
ViewBag.Title = "Cookies and only Cookies";
var cookies = _repository.GetAllCookies().OrderByDescending(x=>x.Price);
return View(cookies);
}
Next is to update the view to read this title
value. Here is the code for this:
Now, if you will run the application, you would be able to see the below output:
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:
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:
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 scope 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:
@{
Layout = "_Layout";
}
Now you can see that this file has already plugged Layout
for us, which means line of code in our view is further reduced to just few lines as shown below:
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:
public class HomeViewModel
{
public string Title { get; set; }
public List<cookie> Cookies { get; set; }
}
Accordingly, we must update our Index
method inside HomeController
as shown below:
public IActionResult Index()
{
HomeViewModel viewModel = new HomeViewModel
{
Title = "Cookies and only Cookies",
Cookies = _repository.GetAllCookies().OrderByDescending(x => x.Price).ToList()
};
return View(viewModel);
}
Lastly, let’s update the View
with reference of HomeViewModel
as shown below:
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:
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:
body {
padding-top:50px;
padding-bottom:20px;
background-image: url();
background-repeat: repeat;
}
.body-content{
padding-left: 15px;
padding-right: 15px;
}
Finally, we have to associate css and bootstrap to our _Layout page using <Link>
as shown below:
Next is to update view file to accommodate bootstrap. Below is the complete code:
If everything was done well, you will see the below web page on running the 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:
public class DatabaseContext:DbContext
{
public DatabaseContext(DbContextOptions<databasecontext> options):base(options)
{
}
public DbSet<cookie> Cookies { get; set; }
}
DataContext
plays a vital role of connecting our application to the actual database. Till now, 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:
public class Repository:IRepository
{
private readonly DatabaseContext _dbContext;
public Repository(DatabaseContext databaseContext)
{
_dbContext = databaseContext;
}
public Cookie GetCookie(int id)
{
return _dbContext.Cookies.FirstOrDefault(x => x.Id == id);
}
public IEnumerable<cookie> GetCookies()
{
return _dbContext.Cookies;
}
}
Next is to set up the connection string. Hope most of you are aware that ASP.NET Core no more uses Web.Config file, 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:
{
"ConnectionStrings": {
"DefaultConnection":
"Server=(localdb)\\MSSQLLocalDB;Database=CookiesDataStore;
Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
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:
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DatabaseContext>(options => options.UseSqlServer
(Configuration.GetConnectionString("DefaultConnection")));
services.AddTransient<IRepository, Repository>();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
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 web page, we need a 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:
public static class DbInitializer
{
public static void Seed(DatabaseContext dbContext)
{
if (!dbContext.Cookies.Any())
{
dbContext.AddRange(
new Cookie
{
Name = "Choco Chips",
Price = 80,
TinyDescription = "Dark chocolate overloaded",
FullDescription = "This is one of the most moist
and flavourful cookie,
which can make anyone's mood happy.",
IsCookieOfTheDay = false,
ImageUrl = "\\Images\\Chocochip.png"
},
new Cookie
{
Name = "Nuts & Peanuts",
Price = 75,
TinyDescription = "Truely healthy",
FullDescription = "This cookie is fully loaded
with nuts of various types and
contain nice amount of peanut butter.",
IsCookieOfTheDay = true,
ImageUrl = "\\Images\\ChocolateChipWalnut.png"
},
new Cookie
{
Name = "Berries & Rasins",
Price = 50,
TinyDescription = "Amazingly fruity",
FullDescription = "This is one of the best ever soft
and chewy cookie and also been awarded
as the best cookie several times.",
IsCookieOfTheDay = false,
ImageUrl = "\\Images\\Nuts.png"
},
new Cookie
{
Name = "Coconut",
Price = 100,
TinyDescription = "Truely healthy",
FullDescription = "This cookie is best suited
for the nut lovers. It's less sweet and
gives very elegant taste.",
IsCookieOfTheDay = false,
ImageUrl = "\\Images\\Coconut.png"
}
);
}
dbContext.SaveChanges();
}
}
Next is to call this DbInitializer
from Program
class and here is the updated code for the same:
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<databasecontext>();
DbInitializer.Seed(context);
}
catch (Exception ex)
{
}
}
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 selected cookie. To get the details of a selected cookie, a new method has to be added in HomeController
class as shown below:
public IActionResult Details(int id)
{
var cookie = _repository.GetCookie(id);
return View(cookie);
}
And of course, we have to add the view for this method with the name Details.cshtml under HomeController
:
Now, we want to navigate to Details
view from Index
view. So, we will use tag helpers as shown below:
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:
Now quickly run the application and you will be able to see the links on top of the page as:
Adding Form to Request Comments
Now, let’s add a form by which 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:
public class Feedback
{
public int Id { get; set; }
public string Name { get; set; }
public string Content { get; set; }
public string Email { get; set; }
}
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:
public class FeedbackRepository: IFeedbackRepository
{
private readonly DatabaseContext _dbContext;
public FeedbackRepository(DatabaseContext context)
{
_dbContext = context;
}
public void AddFeedback(Feedback feedback)
{
_dbContext.Feedbacks.Add(feedback);
_dbContext.SaveChanges();
}
}
Next is to register this new interface with dependency injection container under ConfigureServices
method:
public void ConfigureServices(IServiceCollection services)
{
…
services.AddTransient<IFeedbackRepository, FeedbackRepository>();
services.AddMvc();
}
Now it’s time to make UI changes for feedback functionality. Below is the code for FeedbackController
and its view:
public class FeedbackController : Controller
{
private readonly IFeedbackRepository _feedbackRepository;
public FeedbackController(IFeedbackRepository feedbackRepository)
{
_feedbackRepository = feedbackRepository;
}
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(Feedback feedback)
{
_feedbackRepository.AddFeedback(feedback);
return View();
}
}
Next is to add the Feedback link on our application front page and that can be done by updating Layout
page as shown below:
On click of Feedback
, navigation will occur on below page:
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 interit 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:
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.
Post above action, we will get below dialog which mentions about 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:
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) => {
services.AddDefaultIdentity<identityuser>(IdentityUser)
.AddEntityFrameworkStores<databasecontext>(DatabaseContext);
});
}
}
By the above code, we are using our database context to save identity information. Finally, we have to provide link on our navigation bar, so just plug the login stuff to _Layout
page by going with <Partial>
as shown below:
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
Till here, we have provided login functionality but what about restricting any user from using some functionality of 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:
[Authorize]
public class FeedbackController : Controller
{
……
}
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