[ASP.NET for Beginner] - Part 4 - CRUD và Data Validation

So you have a basic idea of MVC, how to connect to a database, and you picked a front end that fits — it’s time to get your hands on the 4 most basic operations of any web app: CRUD

See the posts in this series

1. CRUD

CRUD is short for the 4 database-related actions: Create, Read, Update and Delete

This post assumes you use Entity Framework, as covered in part 2, and that the example code is already there. You can clone the code from Github here

1.1. DbContext

In the mvcbasic example, MvcBasicDbContext was already created for you and declared in each Controller that needs it. We’ll use this class

In the PhoneController class already created in the example, you can see all 4 of these methods written out.

1.2. Create

To create a record in the database with Entity Framework, things are very simple

var phone = new Phone
{
    Name = "Samsung Galaxy A5 (2017)"
};
 
_context.Phones.Add(phone);
_context.SaveChanges();

If you want to add many rows at once, Entity Framework supports another method

var phones = new List<Phone>();
 
var phone = new Phone
{
    Name = "Phone 1"
};

phones.Add(phone);
 
phone = new Phone
{
    Name = "Phone 2"
};

phones.Add(phone);
 
_context.Phones.AddRange(phones);
_context.SaveChanges();

1.3. Read

To get 1 row of data from a table you can use the Find method

var id = 1;
var phone = _context.Phones.Find(id);

The Find method looks in the Phone table for the primary key = your id. It returns null if that key doesn’t exist

To get many rows matching some condition, you can use LINQ to query

var searchKey = "samsung";
var phones = _context.Phones.Where(x => x.Name.Contains(searchKey));

x stands for one Phone object in the database

x.Name.Contains(searchKey) means Name contains the searchKey string

Almost every query you’re familiar with writing in SQL can be written in LINQ form. This is called LINQ to Entities (using Linq to query through Entity Framework’s entities)

You can find the matching Linq statements with the keyword: “How to the action you want using linq”

The result of a query is of type IQueryable. You can keep filtering it, grouping it, filtering again, or converting it into another type like a List, an Array, etc

You can read more about the supported Linq statements here: Supported and Unsupported LINQ Methods (LINQ to Entities) on docs.microsoft.com

1.4. Update

Not as simple as the other statements: updating with Entity Framework is a bit complicated (only a bit)

1.4.1. The Simplest - 2 trip to database

The simplest way to update 1 record has 3 steps: 1. pull that record from the database - database trip 1. 2. change some information in the record. 3. update that record in the database - database trip 2

its code is

// get the phone object from database
var phone = _context.Phones.Find(phoneId);
 
// change some info
phone.Name = "Test"
 
// update the change to database
_context.SaveChanges();

As you see, this approach forces you to pull the data from the database before you can edit it

1.4.2. A more complicated

So what if you already know all of the object’s data? In that case querying that record from the database isn’t needed

// Create the object from your brain
var phone = new Phone
{
    Id = 1
};
 
// Attach it to DbContext, so the DbContext can "track" the object
_context.Phones.Attach(phone);
 
// Do some change
phone.Name = "Test";
 
// Save the change
_context.SaveChanges();

1.4.3. Track???

Entity Framework has a very nice (and slightly complicated) way to optimize the data reading/updating methods: it tracks an object’s changes

Say your object has 50 fields. You only change 1 of them, then call _context.SaveChanges(); EF knows only 1 field changed and updates exactly that one field, improving the app’s performance.

To do this, it first queries that object by the primary key, and when you call Attach it starts tracking your object.

When you change a field, that field’s state goes from Unmodified to Modified

1.5. Delete

Like edit, when you want to delete an object you also need “2 trips” to the database

var phone = _context.Phones.Find(id);
 
_context.Phones.Remove(phone);
_context.SaveChanges();

2. DbContext - A better way to write it

2.1. What if CRUD failed?

There are many cases where calling the crud statements with entity framework adds or changes no rows at all. So how do you know when it succeeded and when it didn’t?

_context.SaveChanges();

Luckily the method above returns the number of changed records

So you can check very easily with

// your code here to CRUD
var result = _context.SaveChanges();

by checking “result > 0” you know whether your save changes statement succeeded

2.2. await or not?

In the controller code generated by asp.net you’ll see the statements using _context all have async behind them, await in front, and the method’s return type is Task

Simply put, async - await is a keyword pair that simplifies multi thread programming.

If you haven’t got a solid grip on the async-await technique, I’d suggest… dropping it entirely and only using the methods shown in the code above in this post

3. Model Validation

You have surely heard of constraints like

The name cannot be longer than 20 characters. The phone number must have 10 digits

Do you wonder “how did they do that?”

3.1. DataAnnotation

ASP.NET lets you constrain data through DataAnnotations written above each property

Take the Phone model: I want to add a constraint that the name can’t exceed 50 characters, and to report an error when the user types more than that

namespace mvcbasic.Models
{
    using System.ComponentModel.DataAnnotations;
 
    public class Phone
    {
        public int Id { get; set; }
 
        [StringLength(50, ErrorMessage = "Name cannot be more than 50 characters")]
        public string Name { get; set; }
    }
}

You can dig deeper into writing one shared error message for all properties of the same kind here: Error Message – chung mà riêng

To check whether an object meets the model’s conditions you can use ModelState. For example in the Create method

// POST: Phone/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.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] Phone phone)
{
    // CHECK MODEL STATE HERE
    if (ModelState.IsValid)
    {
        _context.Add(phone);
        await _context.SaveChangesAsync();
 
        var phone2 = new Phone
        {
            Name = "Test"
        };
 
        var phones = new List<Phone>();
 
        _context.Phones.AddRange(phones);
 
        return RedirectToAction(nameof(Index));
    }
    return View(phone);
}

3.2. Client side validation

Client side validation lets users see data errors as they type, before any data is sent to your server

What you have to do is add the following lines to your View code

<!--this line may already be in your View/Shared/_Layout.cshtml file-->
<a href="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.0.min.js">https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.0.min.js</a>
 
<a href="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.16.0/jquery.validate.min.js">https://ajax.aspnetcdn.com/ajax/jquery.validate/1.16.0/jquery.validate.min.js</a>
<a href="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js">https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js</a>

and in the form you add the following code to display the error messages (if any)

<form asp-action="Create">
    <!--this line shows a summary message of all the errors-->
    <div class="text-danger"></div>
    <div class="form-group">
 
 
        <!--this line shows the specific error message for the form-->
        <span class="text-danger"></span>
    </div>
    <div class="form-group">
 
    </div>
</form>

The error shows up like this

bug

You can see all the annotations here: Data Annotation on docs.microsoft.com

That’s it. Stay tuned for the next part