Subscribe Now: Feed Icon

Monday, March 4, 2013

HTML5+MVC Course, Day 3

Continued from day 1, day 2.

This lesson was more a review of the last lesson with the added value of looking at profiling tools. In the summary I just skipped the review part…

 

After opening a new MVC project from a template looking at the libraries installed you will find that they are old libraries. You can update them if you wish using NuGet –> Update:

NuGetUpdates

NuGet knows which libraries it needs to update by looking at the packages.config file that describes the installed packages and their versions. For example:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="EntityFramework" version="5.0.0" targetFramework="net45" />
  <package id="jQuery" version="1.7.1.1" targetFramework="net45" />
  <package id="jQuery.UI.Combined" version="1.8.20.1" targetFramework="net45" />

 

Web Programming must have:

  1. Browser Sniffer with a capability to see the HTML – Chrome F12
  2. Client side Sniffer that displays the data as it passes (optional: with JSON parser, with debugging, comparison) – Fiddler
  3. Server side: DB profiler for queries to the DB – SQL Server Profiler

 

Fiddler

FiddlerOverview

Clicking on the “Capturing” in the bottom left corner (pink arrow) will stop and start the capturing.

Clicking on the X (orange arrow) will give you in it’s first option: Remove all. Clears the screen.

HTTP Results:

  • HTTP 100-199 are for information
  • HTTP 200-299 are for successful result, for example: 200 is for success (green arrow)
  • HTTP 300-399 are for redirect, for example: 304 (blue arrow) indicates the result is in browser’s cache
  • HTTP 400-499 are for client errors, for example: 404 page not found
  • HTTP 500-599 are for server errors, for example: 503 server unavailable

On the top-right side of the screen Inspectors->raw we can see the request with it’s http headers

On the bottom-right side of the screen: raw if the message is encoded you will see gibberish (and the Content-Encoding will be gzip):

HTTP/1.1 200 OK

Content-Encoding: gzip

Content-Length: 44892

��������`I�%&/m�{J�J��t��`$ؐ@������iG#)�*��eVe]f@�흼��{���{���;�N'���?\fdl��J�ɞ!���?~|?"��O�<y���<M��<���������ⳏ��

*** FIDDLER: RawDisplay truncated at 128 characters. Right-click to disable truncation. ***

Then:

FiddlerBinary

Click on “Response is encoded and may need to be decoded before inspection. Click here to transform” (blue arrow). Or do it by hand by going to the Transformer tab and select “No Compression” in HTTP Compression.

Ajax+JSON Calls:

You can identify Ajax calls by:

FiddlerAjaxJson

Ajax calls will have: X-Requested-With: XMLHttpRequest (blue arrow).

JSON calls will have: Accept: application/json (green arrow), we can also see that the response has the Content-Type we requested application/json (bottom row).

The JSON response can be read several ways:

1. Text/Raw view:

[{"value":"ALFKI","label":"Alfreds Futterkiste"},{"value":"ANATR","label":"Ana Trujillo Emparedados y helados"},{"value":"ANTON","label":"Antonio Moreno Taquería"},{"value":"AROUT","label":"Around the Horn"}]

Raw will also show the headers.

2. JSON view:

FiddlerResponseJsonView

Views the JSON as a data structure – more readable.

 

Chrome F12 Profiler

Console

From the Console you can run jQuery queries like you can run code in the Immediate of VS:

ChromeF12Console

Console will also show JavaScript errors:

ChromeF12ConsoleException

We will know there is an error by the error icon at the bottom-right corner (red arrow).

We will get the exception details like: from where it was thrown and the description at the center (orange arrow).

And most importantly we will get a link to the actual location in the code the error was thrown from (blue arrow), clicking on it will lead us to _ window:

ChromeSourcesDebugException

As you can see the error icon appears on all screens (red arrow).

You can see the location of the error (yellow arrow).

And you can watch the local variables at the right side (blue arrow).

You can also add more functionality with extensions (for example PageSpeed).

Timeline

very important!

ChromeF12Timeline

At the bottom there is a button for starting the profiler (red arrow).

At the top you can select the timeframe, my browser default for some unknown reason was the first 1 second – so the default is not seeing anything (use the lines at the blue arrows to adjust the time frame).

Profiles: Memory leak checker

ChromeF12ProfileTakeSnapshot

Take heap snapshot at the start and you get the current memory profile:

ChromeF12ProfilesSnap1

As with most memory profiling tools you can sort (blue arrow) by object count, shallow size (the size of the element by itself), retained size (the size of the element including links to other classes). You can read more on the size differences here, but we will use retained size.

You can also drill down to the actual function/line that takes the memory (green arrow).

Take another snapshot and you can compare snapshots:

ChromeF12ProfilesCompare

At the bottom change from Summery to Comparison (blue arrow) and all the view changes to a delta between the runs.

 

Note: IE has it’s own profiler but it seems to be for running time and not for memory leaks.

 

Entity vs. Class

Entity has to have a unique identifier – Key.

Data/Class is without an identifier, JSON uses data not entities (only Silverlight has entities in the client)

If we were to return an Entity with JSON:

public JsonResult GetCustomersCircularError(string term)
{
    var results = from c in db.Customers
                  where c.CompanyName.StartsWith(term)
                  select c;

    return Json(results.ToList(), JsonRequestBehavior.AllowGet);
}

The parser might fail with a circular reference exception:

JsonCircularReferenceError

Instead we have 2 options:

1. POCO: Create a class (View Model) for the return value – one without circular references.

public class JsonCustomer
{
    public string value { get; set; }
    public string label { get; set; }
}

public JsonResult GetCustomersClass(string term)
{
    var results = from c in db.Customers
                  where c.CompanyName.StartsWith(term)
                  select new JsonCustomer { value = c.CustomerID, label = c.CompanyName };

    return Json(results.ToList(), JsonRequestBehavior.AllowGet);
}

2. Use Linq+Anonymous Classes to return that data from the DB entities:

public JsonResult GetCustomers(string term)
{
    var results = from c in db.Customers
                  where c.CompanyName.StartsWith(term)
                  select new { value = c.CustomerID, label = c.CompanyName };

    return Json(results.ToList(), JsonRequestBehavior.AllowGet);
}

Yair believes the second option is best because the class will only be used for returning values (you can’t use it in the client because the client is in JavaScript – not C#).

 

Demo: AutoComplete+Redirect

AutoComplete box that when an item is selected the page is redirected to another and the value of the AutoComplete is passed to the new page.

Server side:
public ActionResult OrdersForCustomers(string id)
{
    var orders = db.Orders.Where(ord => ord.CustomerID == id).Include(o => o.Customer);
    return View("Index", orders.ToList());
}
Client side:

We will use the select event of AutoComplete box. Since we don’t really know the event arguments lets debug them to see their values:

<body>
    <input id="txtCustomer" />
    <script>
        $("#txtCustomer").autocomplete({
            source: "orders/GetCustomers",
            select: function (event, ui) {
                alert("Breakpoint here!");
            }
        });
    </script>
</body>

Note: putting the script tag inside the HTML body is another way to write $(document).ready(function(){…

We will add a breakpoint at the alert to see the argument values:

ChromeF12DebugSelect

As we can see the event argument contains information on the event itself: who called it, from where on the screen, the control name…

The ui argument is what we need it contains both the value and the label of the item selected.

Now the only thing left is to do the redirect:

<script>
    $("#txtCustomer").autocomplete({
        source: "orders/GetCustomers",
        select: function (event, ui) {
            var url = 'Orders/OrdersForCustomers/' + ui.item.value;
            window.location = url;
        }
    });
</script>

And the page will change to the correct URL.