Sunteți pe pagina 1din 67

https://www.codeproject.

com/articles/841324/asp-net-mvc-web-app-on-tier-for-beginners-part-1

ASP.NET MVC Web App on 3-Tier for


Beginners – Part 1
ASP.NET MVC Web App on 3-Tier for Beginners – Part 1

 Download script - 7.1 KB


 Now you can download the complete source code from codeplex

Objective

The objective of this article series is to make the beginners understand how data moves from
one layer to another layer in ASP.NET MVC web app when it is designed on 3-layers.

Overview

Q. Do you want to see a live web app built on 3-Tier arch using ASP.NET MVC, jQuery,
Bootstrap, C#.NET, Entity Framework and Microsoft SQL Server?
A. Here it is http://linkhub.manzoorthetrainer.com/.

Q. Are you really excited to learn how to develop this 3-tier web app on ASP.NET MVC and
Entity Framework?
A. Then you are going to learn it here with me from scratch.

In this article, we are going to learn how to break requirements into objects and find relation
among them. We will then learn designing database out of these objects and relationships.
Moving forward, we will also see how to design a basic 3-Tier architecture of the project and
implement various layers. We are going to learn implementation of business object layer
using Entity Framework and data access layer using repository design pattern and Entity
Framework.

For user interface, we will see ASP.NET MVC, jQuery and Bootstrap. We will learn how
bootstrap makes our application responsive. We will also see how to implement custom
sorting and paging. Moving ahead, you will learn forms validations and business rules
validations as well. We are going to secure our app using custom membership provider and
role provider and publish it on the web.

In Advance operations, we will try to understand how to implement transactions, bind


multiple models to single view and Ajaxifiying our ASP.NET web app. Finally, we will also
see few SSRS client side reporting.

Tools Requirements

 SQL Server 2008 Express or higher


 VS2013 Express or higher
Target Audience

 Should be good at OOPs through C#.NET


 Should have good understanding of ASP.NET MVC and Entity Framework

Disclaimer

The sample code is solely based on my self-study, research results and based on any practical
project. The code is just for the purpose of explaining the overall architecture only. If you
want to use the code in your project, you need to enrich and modify it to meet your own
needs. In addition, the CRUD operations in the sample project are very primitive with basic
features, you can enrich them with some advanced features.

Content
1: Introduction

Introduction

2: Analysis And Design

Requirements Gathering, Identifying Objects And Relationships, Designing Database,


Database Implementation And Understanding Architecture, Creating Solution And Projects.

3: Implementing All the Layers

Creating Business Object Layer Using Entity Framework, UI Prototyping, Designing


Controllers And Actions, Implementing Bootstrap

4: Implementing User And Admin Module

Creating Data Access Layer Using Repository Design Pattern, Creating Business Logic Layer
And Implement BrowseUrLs, Filtering BrowseURLs, Implementing Custom Sorting in MVC
on BrowseUrls table, Implementing Custom Paging in MVC on BrowseUrls table,
Implementing ListUsers, ListCategory And DeleteCategory

5: Architectural Enhancements And Validations

Implementing CreateCategory With Forms Validation, Implementing SubmitUrl And Adding


Base Class in BLL - AdminBsClass, Creating Base Classes For BLL And Controllers,
Implementing Approve Urls And UserRegistration

6: Securing ASP.NET MVC Web App

Implementing Authentication-I, Implementing Authentication-II, Implementing


Authorization
7: Applying Bootstrap Theme

Applying New Bootstrap Theme And Implementing Slider-I, Applying New Bootstrap
Theme And Implementing Slider-II

8: Implementing Transactions

Binding Multiple Models To A Single View, Working With Identity Field, Transactions

9: Ajaxifying An MVC App

Ajaxifying Demo, Making A JQuery Based Ajax Call, Implementing Approve And Reject
ALL With Update Progress Bar, Partial Page Update In MVC

10: External Login

Login With Gmail, Login With Facebook

11: The Final Push

ProjectSetup - Source Code, Publishing Your Site Live

12: Reports In ASP.NET MVC

RDLC Reports In ASP.NET

1: Introduction

Link hub is a web portal where a user can submit her/his portal URL under a specific
category to be shared on link hub. Admin can approve or reject the URL submitted by the
user and in each case an email is sent out to the user. Once the link is approved, it will be
available on the link hub portal.

This is what we are going to achieve:


Screen 1. User Registration

Screen 2. User Login


Screen 3. Submit URL After Login

Screen 4. URL approval by Admin


Screen 5. Browse All The Approved URL

Note: In future articles, we will also try to see some extra features whose screens are not
described here.

2: Analysis And Design (Requirements Gathering, Identifying Objects And


Relationships, Designing Database, Database Implementation)

Let us say that the client has given us brief requirements or you can also call it as user story,
i.e., they need to develop a portal called as LINKHUB…

Link hub is a web portal where a user can submit his/her portal URL under a specific
category to be shared on link hub. Admin can approve or reject the URL submitted by the
user and in each case an email is sent out to the user. Once the link is approved, it will be
available on the link hub portal.

Step 1

From the above requirements, let us define the roles and responsibilities first.

Defining the Roles & Responsibilities Roles

 User
o Can Browse URLs
o Can Register
o Can Submit A URL
 Admin
o Can CRUD Category
o Can View All Users
o Can ApproveOrReject URL

Step 2

Now let us identify the object.

Objects

 User
 Category
 URL

Step 3

Now we need to find the relationship among these objects

Relationships

 Category: Url (1:M) (As a single category can have many URLs)
 User: Url (1:M) (As a single user can upload many URLs)

Step 4

Once we have objects and relationships with us, we can go designing the database with the 3
key rules of database design.

3 Key Rules For Database Design

1. One table for each object


2. For 1:M relationship. 1 will become master and M will become child, i.e., primary key of 1
will act as a foreign key for M.

E.g.: Department : Employees is 1 : M Department


3. M:M relationship. Both the objects will become master and there will be one more new
table called as transaction table or child table with primary keys of masters as foreign Keys in
it.

E.g.: Student : Course is M : M

With these above three rules, our database design would look something like this and role
column in the tbl_User table will differentiate a normal user as ‘U’ and Admin as ‘A’.
LinkHubDb

Let us implement it:

Step 5

Once we have our database ready, we need to insert few meaningful and dummy records
which will help us in further implementation process.
Understanding the Architecture

Data Access Layer

In the above architecture, our application’s back end is Microsoft SQL Server and to access
the data from the database we are using ADO.NET Entity Framework and we call it as Data
Access Layer (DAL).

Business Logic Layer

Before I store data into the database or after reading data from the database, I need to perform
some business logic on the data. For example, I want to store the salary of an employee and it
is $25 per hour and say he is worked for 50hrs. So, his salary would be calculated as
25X50=1250$ which is called as business logic and this logical code will go to business logic
layer and it will be developed using C#.NET programming language. So, here it is Business
Logic Layer (BLL).

UI

Now, how do I interact with the application? Yes, the interface from which I interact with the
application is called as user interface of presentation logic layer where we see all the form
controls like textboxes, buttons, grids, etc., and we are going to use ASP.NET MVC 5 to
implement our Presentation Logic Layer (PLL).

Business Object Layer

And finally, how do I pass the data from one layer to another layer? We need a container to
store the data so that we can pass that container to other layers and that container is called as
business object and they reside in Business Object Layer (BOL).
Therefore, we need to have a solution which should contain the following four projects:

1. UI (ASP.NET MVC Web App)


2. BOL (C# Class Library to generate a DLL)
3. BLL (C# Class Library to generate a DLL)
4. DAL (C# Class Library to generate a DLL)

That’s it for the day. In our next article, we will see Creating Solution And Projects.

Thanks for reading.

https://www.codeproject.com/articles/846054/asp-net-mvc-web-app-on-tier-for-beginners-part-2
ASP.NET MVC Web App on 3-Tier for
Beginners – Part 2
ASP.NET MVC Web App on 3-tier for beginners

Introduction

This article is the continuation of Part 1. In the first article, we saw understanding of
requirements and then breaking them into objects, then finding out the relationships among
them and finally designing and implementing the database. Now, let us see how to create a
solution, then add multiple projects to it and then add desired references to the respective
projects.

2: Analysis and Design (Creating Solution and Projects)

Create a new project in Visual Studio as LinkHubUI, choose desired path and click OK.

Select Empty project, check the MVC check box and click on OK.
Now, we will add some list of tasks which will make us implement the project easily. We
will add a text file, right click on project, add Add New Item and name it as ToDoList.txt.
This ToDoList contains the list of tasks that we need to perform.
This is a step-by-step process to implement the layered architecture in MVC. I’ve done a little
hard work on it to give you these steps. So, I’m sure this is very much helpful for you when
you are implementing a project for the first time.

Now our first task is creating a solution with all projects.

Right click on solution and add new project. Choose from C# -> Class Library and name it as
BOL (Business Object Layer).
Remove Class1.cs file from BOL project.

Add one more Class Library project and name it as BLL (Business Logic Layer) and delete
Class1.cs from BLL project.
Finally, add one more new Class Library project and name it as DAL (Data Access Layer)
and delete Class1.cs file from DAL.
Our Project Architecture

Now, if we look at our architecture, you can see that UI is interacting with BLL and BOL.

So, I need to add BLL.dll and BOL.dll in UI.

BLL is interacting with BOL and DAL. Add BOL.dll and DAL.dll references to BLL.
DAL is interacting with BOL. Add BOL.dll reference to DAL.

If I try to add reference of BLL.dll for DAL, there will be an error (Circular Dependency).

So, we should add the references moving from left to right as per our Arch (Start from Left
layer and see with what layer it is interacting with in right and add the DLL along with
common object layer which is on top). Save All and Rebuild Solution.
Now, we are set with all projects and DLLs. We are done with the first step in ToDoList. In
our next article, we will see creating BOL with the help of Entity Framework.

3: Implementing All the Layers


Creating Business Object Layer (BOL)

What actually BOL is and how do we create Business Objects? Actually, BOL is nothing but
almost a replica of your database tables in the form of Classes. So, for one table in database,
we need to have one Class in our BOL or we will have an equivalent Class for each table.

As, we know, that table is nothing but a relation and Class’s instance is an object. So, Object
Relation Mapping (ORM or OR Mapping) is done in BOL. Now, let us take an example
tbl_Department table (Relation) which has got Did (Department ID), DName (Department
Name) and Description as columns.

So, I’m going to create an equivalent class for tbl_Department with properties as Did of
type int, DName and Description of type string. So, we can say Relation, Object and
Mapping between them. I can call it as OR Mapping.

Previously, we used to write these classes. But, with the help of Entity Framework, we will
be generating these classes automatically. Below slid is for single table. What about the tables
with 1 to many Relationship?
Now let us say we have tbl_Department and tbl_Employee table and one Department can
contain n number of employees if I’ve this relation as shown below. How are we going to
implement these as classes?

It is very simple, we are going to create a class tbl_Department. As we know, department


contains n number of employees. So, we will have list of tbl_Employee as a property in
tbl_Department class. And tbl_Employee is another class for table tbl_Employee. And
also we know that each and every Employee belongs to particular department. So,
tbl_Employee class contains one extra property, i.e., object of tbl_Department.

So for one-to-many relationship below is the class structure. Finally what about many-to-
many relationship?
We know that last relationship is many-to-many.

Let us take an example of Student and Course. One student can opt for n number of
courses and one course can be opted by n number of students. In this scenario, we need to
have an extra class to show many to many relationship between tbl_Student and
tbl_Course i.e., tbl_Student_Course. Now you can see that a student opted for a course
is described by Sid and Cid in class tbl_Student_Course.
And also we are mapping tbl_student and tbl_Course in class tbl_Student_Course. One
student can opt for n number of courses. So, tbl_Student class has List of
tbl_Student_Course. And one course can be opted by n number of students.So,
tbl_Course class has List of tbl_Student_Course.

This is bit logical for many-to-many relationship. Anyway, we need not to worry much about
writing all these code as our Entity Framework is going to generate these kind of classes
automatically.

Now let us implement BOL. Right click on BOL and select Add New Item -> Select
ADO.NET Entity Model and name it as LinkHubModel.edmx and click on Add.
Select Generate model from database and click on Next.
Click on New Connection. Set your connection and Select database and Click Next.
Select Entity Framework 6 and click Next.
Now select all the tables and you can see that Model Namespace is LinkHubDbModel -> Click
Finish.
You may see this security warning twice. Say OK.

Now, you can observe classes that it has generated for each table.
For tbl_Category, it has generated the properties CategoryId, CategoryName and
CategoryDesc.

As we know that one category contains list of Urls, I get List of tbl_Url.

Hide Copy Code


namespace BOL
{
using System;
using System.Collections.Generic;

public partial class tbl_Category


{
public tbl_Category()
{
this.tbl_Url = new HashSet<tbl_Url>();
}

public int CategoryId { get; set; }


public string CategoryName { get; set; }
public string CategoryDesc { get; set; }

public virtual ICollection<tbl_Url> tbl_Url { get; set; }


}
}

For tbl_Users, the properties UserId, UserEmail, Password and Role has been generated.
One user can upload n number of Urls. Again, I’ve a list of Urls.

Hide Copy Code


namespace BOL
{
using System;
using System.Collections.Generic;

public partial class tbl_User


{
public tbl_User()
{
this.tbl_Url = new HashSet<tbl_Url>();
}

public int UserId { get; set; }


public string UserEmail { get; set; }
public string Password { get; set; }
public string Role { get; set; }

public virtual ICollection<tbl_Url> tbl_Url { get; set; }


}
}

Same logic it is following that we learned earlier and let us look into Url class. Class tbl_Url
has properties UrlId, UrlTitle, Url, UrlDesc and so on. As we know that each Url belongs
to a particular category and it is uploaded by a particular User. So, it is following the same
rule.

Hide Copy Code


namespace BOL
{
using System;
using System.Collections.Generic;

public partial class tbl_Url


{
public int UrlId { get; set; }
public string UrlTitle { get; set; }
public string Url { get; set; }
public string UrlDesc { get; set; }
public Nullable<int> CategoryId { get; set; }
public Nullable<int> UserId { get; set; }
public string IsApproved { get; set; }

public virtual tbl_Category tbl_Category { get; set; }


public virtual tbl_User tbl_User { get; set; }
}
}

We’ll get these 3 classes. These are our Business objects. Now I’ll rebuild the BOL project.
As we know, we are going to use these Business objects in UI (User Interface). I need
connection string in UI which is available in BOL project -> App.config file. Copy the
connectionStrings section and paste it in web.config of UI (We will cover this in our next
article) and Rebuild the solution. This is a very important step because my UI is going to
interact with BOL and we’ve generated the BOL with the help of Entity Framework.
If we look into TODO list, we are done with Creating BOL and Adding connection string in
UI.

Thanks for reading.

https://www.codeproject.com/articles/1006238/asp-net-mvc-web-app-on-tier-for-beginners-part-3

ASP.NET MVC Web App on 3-Tier for


Beginners – Part 3
ASP.NET MVC Web App on 3-Tier for Beginners – Part 3

Introduction

This article is the continuation of Part 1 & Part 2 .

In the first article, we saw understanding of requirements and then breaking them into
objects, then finding out the relationships among them and finally designing and
implementing the database. And Good news is our first article won the best article of the
Nov 2014 award.

In second article we saw how to create a solution, then add multiple projects to it and then
add desired references to the respective projects. So, finally we are done with our Business
Object Layer. Now let’s have a quick overview of the things that we’ve developed till now
i.e., we are done with our backend, Business Object Layer.

3: Implementing All the Layers (UI Prototyping)

Now in this article, it is the time for us to implement our UI (User Interface) with the help of
ASP.NET MVC.

To implement this again we need to look back into the roles and responsibilities

Roles

 User
o Can Browse URLs
o Can Register
o Can Submit a URL
 Admin
o Can CRUD (Create, Read, Update and Delete) Category
o Can view all the Users
o Can Approve or Reject URL

Keeping all this in mind, Say we’ve designed a prototype you can say UI prototype.

Let’s have a look

Homepage or you can say Dashboard some kind of calendar that I want to show. Normally,
this homepage is accessible by all the users. Whether it is Admin/User/Anybody. This is a
common page.

Category page

The page where I can create category. Normally, Only Admin should have access to this
page.
List Categories Page

This page is also accessed only by Admin. Admin can see what all the categories he has
created.
Register page

Any user can register. Everyone will have access to this page.

Submit URL page

Once user is registered he can submit URL. Only registered users can access this page. You
can say the only those users with role “U” can access.
Approve URLs page

Once the user submitted a URL this will get reflected to Admin. Admin can look for the URL
that has been submitted then Admin can select some URLs and can Approve or Reject. Once
it is approved. It’ll be available in BrowseURLs

This page is accessed only by Admin or The users with role “A”
Browse URLs page

This is accessed by anyone. Anybody can come and browse for the URLs that are available
ListUsers page

This page is available only to Admin

Login page

Either an Admin or an existing user can login from here.


Let me now break this into Modules.

Altogether I have nine pages.

1. Home
2. Category
3. ListCategories
4. Register
5. SubmitURL
6. ApproveURLs
7. BrowseURLs
8. ListUsers
9. Login

Basically this is our process flow. I’ll go for breaking this into various modules

UI Modules

 User
o Can Submit a URL

Before becoming a user he is a guest and after registration he can submit a


URL

 Admin
o Can create category
o Can list categories
o Can list users
o Can Approve URLs
 Common (Without login)
o Home
o Browse URLs
 Security
o Login
o Register

So, basically I get four modules and all the pages get shifted to these modules. In our earlier
ASP.NET web forms we used to create four different folders (User, Admin, Common and
Security) and used to add related aspx pages to these folders. But, here we do not have the
concept of folders. Instead we’ve the concept of Areas. I can add one Area for each module.

Now, what happens if we create Areas?

The URL for SubmitURL page of User Module will look something like this BaseURL/User/
SubmitURL

Example for Admin module

In the same way we’ve Common and Security


Example for Common module

Example for Security module

In our MVC we talk in terms of Areas. I’m going to create 4 Areas. I’ll say 4 UI – Areas.

Let us see how to create these Areas in MVC.

To create an Area. Right click on LinkHubUI à Add à Area

Add Common Area


It has become damn easy to work with Areas in MVC5. Earlier we need to write
configuration settings as shown.

Hide Copy Code


public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Common_default",
"Common/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}

Whereas now your MVC is doing all the URL routing settings for you. You can see that in
Areas I’ve got Common. Common has got structure Controllers, Models and Views i.e.,
MVC.

CommonAreaRegistration.cs is the URL routing file.Earlier we need to write URL routing


code. But, in MVC5 it’ll be created automatically.
Now, let me quickly add three more Areas for (User, Security and Admin)

Rebuild project and then Rebuild Solution.


We’ve completed adding Areas in MVC

Next I need to add all the Controllers for respective Areas.

Let us see how to create controller. How do we know that how many Controllers we need to
create? How to decide how many Actions that Controller should contain?
That is simple let’s have a look at page – SubmitURL How do I define Controller for this and
Actions in that Controller.

Let us see that in our next Article.

Thanks for reading.

https://www.codeproject.com/Articles/1009212/ASP-NET-MVC-Web-App-on-Tier-for-Beginners-
Part
ASP.NET MVC Web App on 3-Tier for
Beginners – Part 4

In this article, let us try to understand and implement master layout and Bootstrap.

Introduction

This article is the continuation of Part 1, Part 2 and Part 3.

In the first article, we focused on understanding requirements and breaking them into objects,
then finding out the relationships among them, and finally, designing and implementing the
database. And (good news!) our first article won the best article of the Nov 2014 award.

In the second article, we saw how to create a solution, add multiple projects to it, and add
desired references to their respective projects. So, finally we are done with our Business
Object Layer.

In the third article, we saw how to implement our UI (User Interface). In this article, let us try
to understand and implement master layout and bootstrap.

3: Implementing All the Layers (Master layout and Bootstrap)

Our next work item or task is adding bootstrap feature.


Let us observe an option whenever I was trying to add View, i.e., It was asking to use a
layout.
It means that it was creating a separate layout for each area.
Layout (in MVC) is nothing but a type of MasterPage (in ASPX webforms). It is creating
separate Master Pages for each Area and I don’t want to have separate layout for each area. I
want to have a common layout for all the Areas. Let us observe that first.

If you look into Shared folder -> layout as shown:

This is the layout you have in Areas -> Admin -> Views -> Shared -> _layout.cshtml.

Hide Shrink Copy Code


<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - My ASP.NET Application</title>
<link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
<link href="~/Content/bootstrap.min.css" rel="stylesheet"
type="text/css" />
<script src="~/Scripts/modernizr-2.6.2.js"></script>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-
toggle="collapse"
data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("Application name", "Index", "Home", null,
new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
</ul>
</div>
</div>
</div>

<div class="container body-content">


@RenderBody()
<hr />
<footer>
<p>© @DateTime.Now.Year - My ASP.NET Application</p>
</footer>
</div>

<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
</body>
</html>

It is set in ViewStart for each area.

In the same sense, in Common, if you observe, you’ll have the same exact layout and it is
setting it in ViewStart.
In the same way, if you look into Security, you’ll have the same layout and setting it in
ViewStart.
Also the same in User:
Now I want to push layout to the Common view folder that I have outside the Area:

Add a new folder in Views and name it "Shared".


I’ll drag one of the layouts and place it in Shared.
In ViewStart, I’ll just remove the "/Areas/User" i.e., it is going to use the share layout of your
common Views folder.

Same way do for all the Areas (Admin, Common, and Security). So each view is now
pointing to this layout MasterPage.

I can name my Application Name as "LinkHub".

Hide Copy Code


@Html.ActionLink("LinkHub", "Index", "Home",
new { Area = "Common" }, new { @class = "navbar-brand" })

Whenever I click LinkHub, it should take me to Index Home as mentioned in the above
statement.

So I need to pass one more parameter for Area. Whenever I click on LinkHub, it will take me
to Common Area and Home Controller and Action Index. Save all and execute and Browse
for URL /Common/Home. If I click on LinkHub, it will be redirecting to the same page.
As we know, the default controller is Home. Now, how do we set the default Area? I want the
default to redirect me to controller Home of Common Area.

It is very simple to do that. I need to edit the route table and change the default Area.
Look for the area and change it to Common.

Hide Copy Code


public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {controller = "Home", action = "Index", id =
UrlParameter.Optional }
);

(RouteTable.Routes[routes.Count - 1] as Route).DataTokens["area"]
= "Common";
}
}

Save and Execute. By default, it should redirect to Common -> Home Controller.
This is how you can set your default Area. Now let’s switch to layout. I want links to all the
controller indexes or all the links. Notice that you have a div tag with unordered list and in
this I’m adding list items.

For example, Home, which is pointing to the Controller Home of Area Common.

I’m adding all the links:

Now, let me execute this. And I get my application name Home, Category, ListCategory.
Submit URL: We created this form in our earlier article.
I’ve linked all the pages, but how about UI? It’s not looking good. Here comes the power of
your Bootstrap. I need to just right click on the LinkHubUI project in solution and say
Manage NuGet Packages.
We need to look online so it requires an internet connection search for bootstrap. Then I’ll
select Bootstrap for MVC 5.1 and Install. It will install Bootstrap. Read and accept the
agreement and say close.
Now if you execute, you should see the magic. It should turn into a beautiful UI.

For Submit URL, you should see an awesome form with good dropdown.
Now, I’m going to show you one more power of Bootstrap -- it is responsive. For any kind of
screen resolution, it is going to work or for any screen size. If I’m trying to access my page
on my mobile, try checking adjusting the browser window size and see that it will adjust all
the link items automatically.

Finally, it has turned into a beautiful Menu.


This is a responsive design that I get with the help of Bootstrap. That’s it for this article.

Thanks for reading.

S-ar putea să vă placă și