further exploring database operations in mvc jim warren, [email protected] compsci 280 s2 2014...

20
Further exploring database operations in MVC Jim Warren, [email protected] COMPSCI 280 S2 2014 Enterprise Software Development

Upload: isaac-moody

Post on 15-Jan-2016

229 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

Further exploring database operations in MVCJim Warren, [email protected]

COMPSCI 280 S2 2014Enterprise Software Development

Page 2: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

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

COMPSCI 2802

Page 3: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

Last lecture… We introduced Language Integrated Query (LINQ)

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>

Handout 03COMPSCI 2803

using (EmployeesContext db = new EmployeesContext()) {

var emp = from e in db.Employees where e.DateOfBirth.Year < 1975 select e;

return View(emp.ToList()); }

Page 4: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

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

And I want to present the user with the meaningful fields from

SELECT * FROM employee,role WHERE employee.IDnum=role.Idnum;Handout 03COMPSCI 2804

employee role

Page 5: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

Extending the model In your .cs file in the Models directory

Add the new DbSet to the DbContext

And add a class definition matching the new table

Fine so far… but what’s the class of the Join result?Handout 03COMPSCI 2805

public class EmployeesContext : DbContext {public EmployeesContext() : base("MySqlConnection") { }

public DbSet<Employee> Employees { get; set; } public DbSet<EmpRole> Roles { get; set; }}

[Table("role")] public class EmpRole { [Key] public int jobnum { get; set; } public int IDnum { get; set; } public string Role { get; set; } }

Page 6: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

The Join In HomeController.cs we can use a LINQ query

Back in the model, we need to define this new class

Handout 03COMPSCI 2806

var ourEmployees =from e in db.Employees

from r in db.Roles where e.IDnum == r.IDnum orderby e.Surname select new EmpforGrid { IDnum = e.IDnum, Surname = e.Surname, GivenNames = e.GivenNames, YearOfBirth = e.DateOfBirth.Year, Role = r.Role};

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;} }

Also perfectly good, and better use of the LINQ language, would be to replace the 2nd FROM and the WHERE with:join r in db.Roles on e.IDnum equals r.IDnum

Page 7: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

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

Handout 03COMPSCI 2807

@{ 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")))

Page 8: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

The result

Handout 03COMPSCI 2808

Page 9: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

‘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

Show the join, and a count(*)

Handout 03COMPSCI 2809

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();

nativeSQLQuery = "SELECT * FROM employee,role WHERE employee.IDnum=role.IDnum;";var empr = db.Database.SqlQuery<EmpforGrid>(nativeSQLQuery);

return View(empr.ToList());

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 in the View. Since I was counting I only want the first (and only) item in the collection that was returned

Since I’ve typed this as EmpforGrid, I can use it as the Model to pass to my View for the WebGrid

Page 10: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

The result

Easily fixed by updating the SQL:nativeSQLQuery = "SELECT *,Year(employee.DateOfBirth) as YearOfBirth FROM employee,role WHERE employee.IDnum=role.IDnum;";

Handout 03COMPSCI 28010

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

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

Page 11: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

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 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 status

Handout 03COMPSCI 28011

Page 12: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

Where we want to go

Handout 03COMPSCI 28012

Page 13: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

And when we click an Edit link…

Handout 03COMPSCI 28013

Page 14: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

The UPDATE Native SQL version

Given that I have a reference to an object emp of class Employee with updated values:

Handout 03COMPSCI 28014

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);...}

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

Then we just .ExecuteSqlCommand (as compared doing . SqlQuery) on the database context to tell MySQL to have at it

Re-establish the database connection

Page 15: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

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.

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 work with relational data using domain-specific objects –http://msdn.microsoft.com/en-us/data/ef.aspx Handout 03COMPSCI 28015

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)

Page 16: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

Putting the update in the MVC context Making an Edit controller

In HomeController.cs we need a new method to ‘catch’ the invocation of the Edit screen

Handout 03COMPSCI 28016

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) return HttpNotFound(); return View(emp); } }

.Find looks up a record from the dbSet based on its primary key (IDnum was set up as such in MySQL and then this was conveyed to the MVC application by the [Key] decorator in the Model

If we picked it off the WebGrid table 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)

We’ll set up the invocation such that the IDnum of the selected record is passed to this handler

Page 17: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

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 filled out / updated by the user

Handout 03COMPSCI 28017

[HttpPost] public ActionResult Edit(Employee emp) { if (ModelState.IsValid) { using (EmployeesContext db = new EmployeesContext()) { db.Entry(emp).State = EntityState.Modified; db.SaveChanges(); } return RedirectToAction("Index"); } return View(emp); }

The Edit screen will post back a reference to the whole updated Employee object, emp. If it’s valid (more on that later!) then we save the update to the database

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

The [HttpPost] decorator signals that this is the one to receive a completed HTML form (the previous one could’ve been annotated as [HttpGet])

Page 18: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

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 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!

Handout 03COMPSCI 28018

Page 19: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

Inspectingthe HTML

All that Razor boiled down to a hyperlink calling /Home/Edit/2 (the Home controller, looking forthe Editmethod witha parametervalue of 2)

Handout 03COMPSCI 28019

Page 20: Further exploring database operations in MVC Jim Warren, jim@cs.auckland.ac.nz COMPSCI 280 S2 2014 Enterprise Software Development

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) FYI: a battery of LINQ examples:

http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b

Now… 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

Handout 03COMPSCI 28020