DEV Community

Cover image for Full Stack Asp.Net Core App (Bootcamp Project) - Part 2 - The Database and (Razor) Pages
Zoltan Halasz
Zoltan Halasz

Posted on

Full Stack Asp.Net Core App (Bootcamp Project) - Part 2 - The Database and (Razor) Pages

This is the continuation of the material from the previous post of the series: https://dev.to/zoltanhalasz/full-stack-asp-net-core-app-bootcamp-project-part-1-introduction-cfb

Database and entities/models

The main entities of the database will be:

  • users: will store the username and their password(not encrypted! bad practice), and their id
  • notes: title, content, userid, color
  • images: noteid, file name.

Let's have a look at the database script, which defines the relationships.(https://github.com/zoltanhalasz/SmartNotes/blob/master/SmartNotes/script.sql)

Using EF Core, the database is scaffolded into a Model folder.

The model classes will look in the following way(as scaffolded by EF Core):

public partial class Users
    {
        public Users()
        {
            Notes = new HashSet<Notes>();
        }

        public int Id { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }

        public virtual ICollection<Notes> Notes { get; set; }
// I will use this to store the confirmed password, not save in the DB
        [NotMapped]
        public string Password2 { get; set; }
    }

public partial class Notes
    {
        public Notes()
        {
            Images = new HashSet<Images>();
        }

        public int Id { get; set; }
        public int Userid { get; set; }
        public string Title { get; set; }
        public string NoteText { get; set; }
        public DateTime Createdat { get; set; }
        public bool Pinned { get; set; }

        public string Color { get; set; }
        public virtual Users User { get; set; }
        public virtual ICollection<Images> Images { get; set; }
    }

    public partial class Images
    {
        public int Id { get; set; }
        public int Noteid { get; set; }
        public string Image { get; set; }
        public virtual Notes Note { get; set; }
    }
Enter fullscreen mode Exit fullscreen mode

For an existing database, it can be scaffolded into a context and models using the following instructions.(https://www.entityframeworktutorial.net/efcore/create-model-for-existing-database-in-ef-core.aspx)

The Github Repo of my project is here. https://github.com/zoltanhalasz/SmartNotes/

The structure of wwwroot folder:

  • css: will contain the manually written css files for each served page
  • images: will contain the images that belong to the html of the pages
  • js and lib: can be empty.
  • uploads: will contain the result of the uploads, images that will appear in each note.

The Pages

The pages are served by Asp.Net Core Razor pages, which is the basic project in Asp.Net Core. (version used here is 3.1, LTS). Each page will have its own css file, present in wwwroot css folder. Their respective html code will be in the cshtml of the Razor Page, only the Notes Page having lots of Javascript included also. I recommend very strongly that you review Razor Pages because they are serving the pages.

The CSS and Html
The Css was written manually (I will not go in detail here) together with the Html, according to a design template. It defines the structure of the files in the view part of the pages below.
Each html file will have its own css. The Layouts will be null in each of the below Razor Pages. You can see the html/css in the Github repo, although this was a part of the bootcamp, I will not go through them.

I will insert more comments here on the blog, than in the Github, to make it more understandable. I will not build the pages step by step, instead just show the comments and explanations regarding the code.

a. Index Page

The PageModel code: - nothing special, here.

The html, you can find on the Github Demo.

b. Signup Page


 public class SignUpModel : PageModel
    {
        private readonly SmartNotesDBContext _context;
        public SignUpModel(SmartNotesDBContext context)
        {
            _context = context;
        }
// below property will contain the user that will be created, linked to the page using binding
        [BindProperty]
        public  Users newUser { get; set; }
// below property will be used to display an error message, linked to the page using binding
        [BindProperty]
        public string errorMessage { get; set; }
// this will display the error message if the user signup did not work well
        public void OnGet(string err)
        {
            errorMessage = err;
        }

        // basic email validation function
        bool IsValidEmail(string email)
        {
            try
            {
                var addr = new System.Net.Mail.MailAddress(email);
                return addr.Address == email;
            }
            catch
            {
                return false;
            }
        }
// checks if any other user has the same email, which have to be unique in the database.
        bool IsExistingEmail(string email)
        {
            return _context.Users.Any(x => x.Email == email);
        }

        // posting the form on the SignUp page, and collecting /saving the user data in the database.
        public async Task<IActionResult> OnPost()
        {
            newUser.Email = newUser.Email.Trim();

            if (!IsValidEmail(newUser.Email))
            {
                errorMessage = "Use a valid email address!";
                return RedirectToPage("./SignUp", new { err  =  errorMessage});
            }

            if (IsExistingEmail(newUser.Email))
            {
                errorMessage = "This Email Address has already been used!";
                return RedirectToPage("./SignUp", new { err = errorMessage });
            }

            if (newUser.Password!=newUser.Password2)
            {
                errorMessage = "The passwords do not match!";
                return RedirectToPage("./SignUp", new { err = errorMessage });
            }

            try
            {
                await _context.Users.AddAsync(newUser);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            { 
                // error message is generated and page redirected
                errorMessage = "Error with signup. Please try again later.";
                return RedirectToPage("./SignUp", new { err = errorMessage });
            }
// when signup was sucessful, user will be redirected to login.
            return RedirectToPage("./Login");
        }

    }
Enter fullscreen mode Exit fullscreen mode

c. Login Page

 public class LoginModel : PageModel
    {

        private readonly SmartNotesDBContext _context;
        public LoginModel(SmartNotesDBContext context)
        {
            _context = context;
        }
// the user who tries to log in
        [BindProperty]
        public Users LoginUser { get; set; }
// the error message which will be shown in the html in case of unsuccesful attempt
        [BindProperty]
        public string errorMessage { get; set; }


        public void OnGet(string err)
        {
            errorMessage = err;
        }
// login, posting the form
        public async Task<IActionResult> OnPost()
        {
            // try to find the user in the table having email and password provided
            var myUser = new Users();
            try
            {
                 myUser = await _context.Users.FirstAsync(x => x.Email == LoginUser.Email && x.Password == LoginUser.Password);

            }
            catch (Exception ex)
            {
                errorMessage = "Invalid User/Password";
                // if no user found, error message shown on page in the form.
                return RedirectToPage("./Login", new { err = errorMessage });
            }
// save the user in the session
            SessionHelper.SetObjectAsJson(HttpContext.Session, "loginuser", myUser);
            // if user found, it's logged in and redirected to notes.
            return RedirectToPage("./Notes");
        }
    }
Enter fullscreen mode Exit fullscreen mode

d. Notes Page

    public class NotesModel : PageModel
    {
// this user id will be used in the html/javascript of the page
        public int LoginUserID { get; set; }
// this email address will be used in the html/javascript of the page
        public string LoginUserEmail { get; set; }

// will take the session value of the logged in user and serve the page. if no user is logged in, will redirect to Login page.
        public async Task<IActionResult> OnGet()
        {
            //check if the user arrived here using the login page, then having the loginuser properly setup
            var loginuser = SessionHelper.GetObjectFromJson<Users>(HttpContext.Session, "loginuser");
            // if no user logged in using login page, redirect to login
            if (loginuser == null)
            {
                return RedirectToPage("./Login");
            }

            //just pickup the user id and email to show it on the page (and use them in the js code), see html code
            LoginUserID = loginuser.Id;
            LoginUserEmail = loginuser.Email;
            return Page();
        }
    }
Enter fullscreen mode Exit fullscreen mode

e. Sign out

 public class LogoutModel : PageModel
    {
        public IActionResult OnGet()
        {
            // logoout page deleting the logged in user and redirecting to main page.
            SessionHelper.SetObjectAsJson(HttpContext.Session, "loginuser", null);
            return RedirectToPage("./Index");
        }
    }
Enter fullscreen mode Exit fullscreen mode

f. Error page
this is mainly a notfound page, written in html/css. Nothing special on the PageModel code.

The Web API back-end of the application will dealt with the CRUD operations, and this will be dealt in the next post.

Top comments (2)

Collapse
 
nickstavrou profile image
NickStavrou

Why do you initialize navigation properties in the constructor as a new HashSet();

Collapse
 
zoltanhalasz profile image
Zoltan Halasz

Its generated by the scaffolding of model from the db.