
As discussed in Part 1 of this blog series, ASP.NET Membership allows you to validate and store user credentials with built-in functionalities. It helps you manage user authentication in the web application. ASP.NET Membership is a self-standing feature for authentication. It can also be integrated with ASP.NET role management to provide authorization services for the web applications. Membership can also be integrated with user profile to provide application-specific customization tailored to individual users.
In this Part 2 of the series, we will address question 3 of the process.
- If you have an existing database, how do you enable it with authentication and authorization? Should you re-invent the wheel, or just use the ASP.NET membership provider?
- If we’re using ASP.NET membership, can we integrate it with existing database?
- How do you create an MVC application, if one does not already exist?
- How does one handle authentication and authorization?
- What is the limitation of the using the MVC5’s Role base data annotation? How do we overcome it?
STEP 3: Building a MVC application using ASP.NET membership, Entity Framework 6 with a Model First approach
1. Create an ASP.NET Web Application – named SchoolManagementSystem, if web application does not currently exist.
2. Under template, select MVC, click “Change Authentication”
3. Under “Change Authentication,” choose “No Authentication.” Click “OK” to create the project.
In this example, we chose “No Authentication.” It might be a bit misleading, but the SchoolManagementSystem application will be configured to manually use ASP.NET membership for authentication. If we select Organizational Accounts, the application will be configured to use Windows Identity Foundation (WIF) for authentication in Azure Active Directory, or Windows Server Active Directory. And Windows Authentication is intended for Intranet web applications, which won’t include user registration or login GUI.
4. Change the site layout by:
Replace content of "~/Views/Shared/_Layout.cshtml" with following:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - School Management System</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</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("School Management System", "Index", "Home", null, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>
</div>
</div>
</div>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>© @DateTime.Now.Year - School Management System</p>
</footer>
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)
</body>
</html>
Replace content of "~/Views/Home/Index.cshtml" with following:
@{
ViewBag.Title = "Home Page";
}
<div class="jumbotron">
<h1>School Management System</h1>
</div>
<div class="row">
<div class="col-md-4">
<h2>WHERE SCIENCE AND ENGINEERING CONVERGE</h2>
<p>
School Management System is a sample application that
demonstrates how to use Entity Framework 6 in an
ASP.NET MVC 5 web application with ASP.NET Membership.
</p>
</div>
<div class="col-md-4">
<h2>Computer Science</h2>
<p>Undergraduate</p>
<p>Graduate</p>
<p><a class="btn btn-default" href="#">Intro »</a></p>
</div>
<div class="col-md-4">
<h2>Applied Mathematics</h2>
<p>Undergraduate</p>
<p>Graduate</p>
<p><a class="btn btn-default" href="#">Tourguide »</a></p>
</div>
</div>
5. You will see the sample application as shown below.
6. Add a new “Class Library” project “SMSDB” to host the Entity Framework Designer and then generate a database schema from the model.
7. Project -> Add New Item…
8. Select “Data” from the left menu and then “ADO.NET Entity Data Model,” name it “SMSDB.edmx.”
9. Select “Generate from database,” hit “Next.”
10. In the “Choose Your Data Connection” dialog box, click “New Connection.” In the “Choose Data Source” dialog box, select “Microsoft SQL Server” as Data Source and select “.NET Framework Data Provider for SQL Server” for the Data Provider.
11. Enter “SampleServer” (or the actual server name) in “Server name” dropdown, type the credentials for SQL Server Authentication, then select “School” from the “Select or enter a database name” dropdown. Click “Test Connection” to make sure your connection to the “School” database is working. Hit “OK.”
12. In the “Entity Data Model Wizard,” choose “Yes, include the sensitive data in the connection string.” Check checkbox “Save entity connection settings in App.Config as,” and use “SchoolEntities” as the name. Hit “Next.”
13. Choose “Entity Framework 6.0,” and hit “Next.”
14. Check “Tables” from the School database, and pick only the following tables: “aspnet_Applications,” “aspnet_Membership,” “aspnet_Roles,” “aspnet_Users,” and “aspnet_UsersInRoles” from the ASP.NET membership. Hit “Finish.”
15. Hit “OK,” if you see the Security Warning dialog.
16. The Entity Framework model below will be generated.
17. Add more properties to the .edmx diagram – Right click in a blank area in the Entity Framework Model Designer -> Add New -> Entity
18. Add “UserPersonPermission” entity as shown below, and hit “OK.”
19. Right click on the new entity, Add New -> Association Property. Enter details as shown below to associate aspnet_Users for the UserPersonPermission table. Select “1” to “*(Many)” relationships for Multiplicity.
20. Right click on the new entity, Add New -> Association Property, enter details as shown below to associate Person table with UserPersonPermission table, which has 1 and (0 or 1) relationship.
21. Add five more scalar properties: IsLocked, DateCreated, CreatedBy, DateUpdated, and UpdatedBy with details shown as noted below.
22. Below is the model view with the newly added properties.
23. Next, right click SMSDB.edmx designer and select “Generate Database from Model.”
24. Click “Finish.”
25. You’ll see the part of the DB script as shown below.
SET QUOTED_IDENTIFIER OFF;
GO
USE [School];
GO
IF SCHEMA_ID(N'dbo') IS NULL EXECUTE(N'CREATE SCHEMA [dbo]');
GO
-- --------------------------------------------------
-- Dropping existing FOREIGN KEY constraints
-- --------------------------------------------------
IF OBJECT_ID(N'[dbo].[FK__aspnet_Me__Appli__45F365D3]', 'F') IS NOT NULL
ALTER TABLE [dbo].[aspnet_Membership] DROP CONSTRAINT [FK__aspnet_Me__Appli__45F365D3];
GO
IF OBJECT_ID(N'[dbo].[FK__aspnet_Me__UserI__46E78A0C]', 'F') IS NOT NULL
ALTER TABLE [dbo].[aspnet_Membership] DROP CONSTRAINT [FK__aspnet_Me__UserI__46E78A0C];
GO
IF OBJECT_ID(N'[dbo].[FK__aspnet_Ro__Appli__6477ECF3]', 'F') IS NOT NULL
ALTER TABLE [dbo].[aspnet_Roles] DROP CONSTRAINT [FK__aspnet_Ro__Appli__6477ECF3];
GO
IF OBJECT_ID(N'[dbo].[FK__aspnet_Us__Appli__35BCFE0A]', 'F') IS NOT NULL
ALTER TABLE [dbo].[aspnet_Users] DROP CONSTRAINT [FK__aspnet_Us__Appli__35BCFE0A];
GO
IF OBJECT_ID(N'[dbo].[FK__aspnet_Us__RoleI__693CA210]', 'F') IS NOT NULL
ALTER TABLE [dbo].[aspnet_UsersInRoles] DROP CONSTRAINT [FK__aspnet_Us__RoleI__693CA210];
GO
……
Go back to SMSDB.edmx designer and save it.
26. Add the folder: DAL in SchoolManagementSystem project.
This will act as a Data Access Layer (DAL) to the database. Then add an interface: ISMSRepository, to the DAL folder.
Add the following codes to ISMSRepository:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SMSDB;
namespace SchoolManagementSystem.DAL
{
interface ISMSRepository
{
void InsertPerson(Person person);
IEnumerable<Person> GetPerson();
Person GetPersonById(int personId);
void UpdatePerson(Person person);
void DeletePerson(Person person);
void Save();
}
}
28. Implement SMSRepository.cs as the following:
namespace SchoolManagementSystem.DAL
{
public class SMSRepository : ISMSRepository
{
private SchoolEntities _dbContext;
public SMSRepository(SchoolEntities dbContext)
{
this._dbContext = dbContext;
}
public void InsertPerson(Person person)
{
_dbContext.People.Add(person);
_dbContext.SaveChanges();
}
public IEnumerable<Person> GetPerson()
{
return _dbContext.People.ToList();
}
public Person GetPersonById(int personId)
{
return _dbContext.People.Find(personId);
}
public void UpdatePerson(Person person)
{
_dbContext.Entry(person).State = EntityState.Modified;
_dbContext.SaveChanges();
}
public void DeletePerson(int personId)
{
var personToBeDeleted = _dbContext.People.Find(personId);
_dbContext.People.Remove(personToBeDeleted);
_dbContext.SaveChanges();
}
}
}
29. Add the class: SMSRepository.cs to DAL as shown below.
30. Add PersonController by selecting “MVC5 Controller with views, using Entity Framework.”
31. Check “Use async controller actions,” select Person (SMSDB) in Model class, SchoolEntities (SMSDB) in Data context class. Click Add.
32. PersonController.cs should appear as below.
namespace SchoolManagementSystem.Controllers
{
public class PersonController : Controller
{
private SchoolEntities db = new SchoolEntities();
[CustomAuthorize("Admin", "Faculty", "Staff", "Student")]
public async Task<ActionResult> Index()
{
var people = db.People.Include(p => p.OfficeAssignment).Include(p => p.UserPersonPermission);
return View(await people.ToListAsync());
}
[CustomAuthorize("Admin", "Faculty", "Staff", "Student")]
public async Task<ActionResult> Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Person person = await db.People.FindAsync(id);
if (person == null)
{
return HttpNotFound();
}
return View(person);
}
[CustomAuthorize("Admin", "Faculty")]
public ActionResult Create()
{
ViewBag.PersonID = new SelectList(db.OfficeAssignments, "InstructorID", "Location");
ViewBag.PersonID = new SelectList(db.UserPersonPermissions, "Id", "CreatedBy");
return View();
}
// POST: /Person/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[CustomAuthorize("Admin", "Faculty")]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include="PersonID,LastName,FirstName,HireDate,EnrollmentDate,Discriminator")] Person person)
{
if (ModelState.IsValid)
{
db.People.Add(person);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.PersonID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", person.PersonID);
ViewBag.PersonID = new SelectList(db.UserPersonPermissions, "Id", "CreatedBy", person.PersonID);
return View(person);
}
[CustomAuthorize("Admin", "Faculty")]
public async Task<ActionResult> Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Person person = await db.People.FindAsync(id);
if (person == null)
{
return HttpNotFound();
}
ViewBag.PersonID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", person.PersonID);
ViewBag.PersonID = new SelectList(db.UserPersonPermissions, "Id", "CreatedBy", person.PersonID);
return View(person);
}
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[CustomAuthorize("Admin", "Faculty")]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include="PersonID,LastName,FirstName,HireDate,EnrollmentDate,Discriminator")] Personperson)
{
if (ModelState.IsValid)
{
db.Entry(person).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
ViewBag.PersonID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", person.PersonID);
ViewBag.PersonID = new SelectList(db.UserPersonPermissions, "Id", "CreatedBy", person.PersonID);
return View(person);
}
[CustomAuthorize("Admin", "Faculty")]
public async Task<ActionResult> Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Person person = await db.People.FindAsync(id);
if (person == null)
{
return HttpNotFound();
}
return View(person);
}
[CustomAuthorize("Admin", "Faculty")]
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(int id)
{
Person person = await db.People.FindAsync(id);
db.People.Remove(person);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
- For those who encounter an error: Incompatible SQL Server version, you need to download SSDT for Visual Studio 2013(16.5). The Person’s page appears after pressing F5 in VS 2013.
In Part 3 of the series, we will address the questions related to enabling authentication and authorization and overcoming limitations of the MVC5’s role base data.