Lecture 3: Powerpoint
Download
Report
Transcript Lecture 3: Powerpoint
COMPSCI 280 S2 2014
Enterprise Software Development
Further exploring database operations in MVC
Jim Warren, [email protected]
Today’s learning objectives
To be able to query through a variety of approaches including
sending SQL directly to the DBMS and via LINQ
To be able to write more advanced queries in the MVC
context, including joining tables and mapping the result to the
View
To be able to update the database from the MVC context
2
COMPSCI 280
Last lecture…
We introduced Language Integrated Query (LINQ)
using (EmployeesContext db = new EmployeesContext())
{
var emp = from e in db.Employees
where e.DateOfBirth.Year < 1975
select e;
return View(emp.ToList());
}
In this case the return value is a collection of objects of a class
we’ve already defined in the Model
Because we had said:
public DbSet<Employee> Employees { get; set; }
and had defined the Employee class with property names exactly aligned to
the columns of the DBMS table
And in the VIEW we had said:
@model IEnumerable<MvcApplication1.Models.Employee>
3
COMPSCI 280
Handout 03
Dealing with a Join
But what if we want to present the user with the result of a
Join on two (or more) tables?
Say that there’s a ‘role’ table which maps ‘employee’ rows by their
primary key IDnum to one or more job roles
employee
role
And I want to present the user with the meaningful fields from
SELECT * FROM employee,role WHERE employee.IDnum=role.Idnum;
4
COMPSCI 280
Handout 03
Extending the model
In your .cs file in the Models directory
Add the new DbSet to the DbContext
public class EmployeesContext : DbContext {
public EmployeesContext() : base("MySqlConnection") { }
}
public DbSet<Employee> Employees { get; set; }
public DbSet<EmpRole> Roles { get; set; }
And add a class definition matching the new table
[Table("role")]
public class EmpRole
{
[Key]
public int jobnum { get; set; }
public int IDnum { get; set; }
public string Role { get; set; }
}
5
Fine so far… but what’s the class of the Join result?
COMPSCI 280
Handout 03
The Join
In HomeController.cs we can use a LINQ query
var ourEmployees =
Also perfectly good, and better use of
from e in db.Employees
the LINQ language, would be to
from r in db.Roles
replace the 2nd FROM and the WHERE
where e.IDnum == r.IDnum
with:
orderby e.Surname
join r in db.Roles on e.IDnum
select new EmpforGrid {
equals r.IDnum
IDnum = e.IDnum,
Surname = e.Surname,
GivenNames = e.GivenNames,
YearOfBirth = e.DateOfBirth.Year,
Role = r.Role};
Back in the model, we need to define this new class
public class EmpforGrid
{
public int IDnum {get; set;}
public string Surname {get; set;}
public string GivenNames {get; set;}
public int YearOfBirth { get; set;}
public string Role { get; set;}
}
6
COMPSCI 280
Handout 03
The View
In Index.cshtml (under Views/Home)
We define the model for the view as an enumerable collection of
instances of the new class:
@model IEnumerable<MvcLetsConnect.Models.EmpforGrid>
And we define the WebGrid itself
@{
ViewBag.Title = "People";
WebGrid grid = new WebGrid(Model);
}
<h2>People</h2>
@grid.GetHtml(columns: grid.Columns(
grid.Column("IDnum","Employee ID"),
grid.Column("Surname","Last Name"),
grid.Column("GivenNames","Given Names"),
grid.Column("YearOfBirth","Birth Year"),
grid.Column("Role","Role")))
7
COMPSCI 280
Handout 03
The result
8
COMPSCI 280
Handout 03
‘Native’ SQL
You can also send ‘native’ SQL direct to the DBMS
Create a string, not interpreted as a query by VS
And send it to the database context with the SqlQuery method
string the_name = "Tom";
nativeSQLQuery = String.Format("SELECT COUNT(*) FROM employee
WHERE GivenNames='{0}'",the_name);
var cnt = db.Database.SqlQuery<int>(nativeSQLQuery);
ViewBag.empcnt = "Count=" + cnt.First();
The SqlQuery returns a ‘generic’
class that takes a type parameter
(I’m counting here, so int is good)
I can just put anything in ViewBag and it’ll be available
Show
the
join,
a count(*)
in the
View.
Sinceand
I was counting
I only want the first
(and only) item in the collection that was returned
nativeSQLQuery = "SELECT * FROM employee,role WHERE
employee.IDnum=role.IDnum;";
var empr = db.Database.SqlQuery<EmpforGrid>(nativeSQLQuery);
return View(empr.ToList());
9
Since I’ve typed this as EmpforGrid, I can use it as
the Model to pass to my View for the WebGrid
COMPSCI 280
Handout 03
The result
Oh, oops! EmpforGrid has a
YearOfBirth not a DateofBirth (as in
the employee table). It forgave me
for leaving it out of the SqlQuery
result and put in a ‘convenient’
default
Easily fixed by updating the SQL:
Note I’m using the SQL syntax
Year() for the conversion, not the C#
syntax which is to invoke the .Year
method on the DateTime object
nativeSQLQuery = "SELECT *,Year(employee.DateOfBirth) as
YearOfBirth FROM employee,role WHERE employee.IDnum=role.IDnum;";
10
COMPSCI 280
Handout 03
What about the rest of the CRUD?!
CRUD
Create, Read, Update, Delete
A CRUD matrix can be a useful specification for the scope of
programming tasks
E.g. to describe the ‘life cycle’ of each entity in a system; e.g. a hotel
reservation
11
On some screen (e.g. ‘booking’) it is created (SQL INSERT, not CREATE
like making a new table)
There may be a screen to update it (e.g. ‘change booking’) which probably
also reads the current value (‘R’ of CRUD, SQL SELECT)
And there will be one or more ways to delete it (e.g. from a cancellation
action on the change booking screen or a dedicated cancellation screen,
and also once the person has checked in) – then again, you might not
actually delete in a SQL sense, but UPDATE it to cancelled or utilised
COMPSCI 280
Handout 03
status
Where we want to go
12
COMPSCI 280
Handout 03
And when we click an Edit link…
13
COMPSCI 280
Handout 03
The UPDATE
Native SQL version
Given that I have a reference to an object emp of class Employee
with updated values:
Re-establish the database connection
using (EmployeesContext db = new EmployeesContext()) {
string sql=String.Format(
"UPDATE employee SET Surname='{0}',GivenNames='{1}',"+
"DateOfBirth='{2:yyyy-MM-dd}' WHERE IDnum={3}",
emp.Surname,emp.GivenNames,emp.DateOfBirth,emp.IDnum);
db.Database.ExecuteSqlCommand(sql);
...
}
Then we just .ExecuteSqlCommand (as
compared doing . SqlQuery) on the
database context to tell MySQL to have
at it
14
COMPSCI 280
Here we’re just building the text
of a SQL command with the help
of String.Format to plug in the
parameters from our C# code at
the curly braces (note the 3rd
parameter - #2 counting from 0! –
is formatted so MySQL
recognises the date literal
Handout 03
The UPDATE v2
Entity Framework* version
Now assuming a bit more about emp… not just that it’s an object
of class Employee, but that it’s already ‘attached’ to the database
context.
using (EmployeesContext db = new EmployeesContext()){
...
}
db.Entry(emp).State = EntityState.Modified;
db.SaveChanges();
The 'State' property determines
what's done by the SaveChanges
method (modified entities are
updated)
As it turns out, this will be a correct assumption once we put this
code into the appropriate part of the MVC application
*Entity Framework (EF) is an object-relational mapper that enables .NET developers to
15
work with relational data using domain-specific objects –
http://msdn.microsoft.com/en-us/data/ef.aspx
COMPSCI 280
Handout 03
Putting the update in the MVC context
Making an Edit controller
In HomeController.cs we need a new method to ‘catch’ the
We’ll set up the invocation such
invocation of the Edit screen
that the IDnum of the selected
record is passed to this handler
public ActionResult Edit(int id)
{
ViewBag.Message = String.Format(
"Your are editing the record for employee ID #{0}.",id);
using (EmployeesContext db = new EmployeesContext())
{
Employee emp = db.Employees.Find(id);
if (emp == null)
.Find looks up a record from the
return HttpNotFound();
dbSet based on its primary key
return View(emp);
(IDnum was set up as such in
}
MySQL and then this was
If we picked it off the WebGrid table
}
conveyed to the MVC application
16
that we ourselves define it really
should be found, but good to handle
anyway (e.g. user might edit the URL
string, or somebody might’ve deleted
it since the user last refreshed their
browser screen)
COMPSCI 280
by the [Key] decorator in the
Model
Handout 03
And we need a second version…
In HomeController.cs we need a second signature for the
method
This version catches the Edit screen when it’s being returned to us
The [HttpPost] decorator signals that this
filled out / updated by the user
is the one to receive a completed HTML
form (the previous one could’ve been
[HttpPost]
annotated as [HttpGet])
public ActionResult Edit(Employee emp) {
if (ModelState.IsValid) {
using (EmployeesContext db = new EmployeesContext()) {
db.Entry(emp).State = EntityState.Modified;
db.SaveChanges();
}
The Edit screen will post back a
return RedirectToAction("Index");
reference to the whole updated
}
Employee object, emp. If it’s valid
return View(emp);
(more on that later!) then we save
}
the update to the database
17
If the data was valid, the edit is done
so redirect back to the home page;
otherwise (there’s some invalid data
on the form), present the form to the
user again
COMPSCI 280
Handout 03
Invoking the edit controller
Putting an ActionLink into the WebGrid
To define the column with the Edit link on each employee:
grid.Column(header: "Edit",
format: (item) =>
Html.ActionLink("Edit", "Edit", new { id=item.IDnum }))
OK, a bit of cryptic Razor syntax here, but note
18
We’re creating an ActionLink (labelled “Edit” and that invokes the
Edit handler in our controller)
It’s per ‘item’ in the WebGrid (one link for each employee) – ‘item’
is just a magic keyword to indicate the entity on the current row
The => is a ‘lambda expression’ (more on that later… it’s a function
as a parameter)
We assign the .IDnum of the current item (i.e. it’s primary key) to
‘id’ – which is the parameter expected by our controller code!
COMPSCI 280
Handout 03
Inspecting
the HTML
19
All that Razor
boiled down to a
hyperlink calling
/Home/Edit/2 (the
Home controller,
looking for
the Edit
method with
a parameter
value of 2)
COMPSCI 280
Handout 03
Where we’re up to
We’ve seen that we can interact with our DBMS by sending it
native SQL or using C#/.NET language-embedded syntax
(LINQ and Entity Framework)
And we can add and invoke additional handlers in our
controller (e.g. for an Edit/Update function)
Now…
20
FYI: a battery of LINQ examples:
http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b
Work the second labsheet (if you haven’t already)
Get seriously into Assignment 2
Next lecture we’ll look at creating the HTML form to get
interactive input from the user for the Edit / Update
COMPSCI 280
Handout 03