String Handling Tips

String Handling Tips When working with strings, you should take advantage of certain classes and methods to avoid performance and memory problems. A key item to remember about .NET string is that they are immutable. Immutable means that strings are read-only and the value cannot be changed after it is created. If you add more data to the original string, a new instance of a string class is created to hold the new string and the old memory is marked for garbage collection. Thus, if you are doing a lot of string manipulation, you can create performance and memory issues in your application.

The StringBuilder Class for Concatenating

One way to avoid these issues is to use the StringBuilder class. The StringBuilder class is mutable so manipulations on strings are much quicker. You may pre-allocate space when creating an instance of the StringBuilder class. If you know you will be concatenating a lot of strings together pre-allocating space allows for growth without having to move memory blocks around. The methods Append, AppendLine and AppendFormat are what you will use to append new data into the StringBuilder object as shown below.

StringBuilder sb = new StringBuilder(1024);
sb.Append("Adding a new String without a line break.");
sb.AppendLine("Adding onto the previous line and add a line break");
sb.AppendFormat("Hello {0}", "Tom"); tbResult.Text = sb.ToString();

Other String Class Methods

A couple of other very useful techniques for working with strings are methods of the string class. The IsNullOrEmpty method helps you check to see if a string is a null value or an empty string. The use of this method is much better than using an "if" statement to check both. For example instead of 

1.	string value = null;
2. Debug.WriteLine(string.IsNullOrEmpty(value));
3.
4. value = "";
5. Debug.WriteLine(string.IsNullOrEmpty(value));
6.
7. value = " ";
8. Debug.WriteLine(string.IsNullOrEmpty(value));
9.
10. value = "Some Text";
11. Debug.WriteLine(string.IsNullOrEmpty(value));

The results of running the above code is "True", "True", "False" and "False". Notice that an empty space on line 7 is not an empty string. If you want to check for all white space within a string use the IsNullOrWhiteSpace as shown in the code snippet below:

string value = null;
Debug.WriteLine(string.IsNullOrWhiteSpace(value));
value = "";
Debug.WriteLine(string.IsNullOrWhiteSpace(value));
value = " ";
Debug.WriteLine(string.IsNullOrWhiteSpace(value));
value = "    ";
Debug.WriteLine(string.IsNullOrWhiteSpace(value));

The results of running the above code is "True", "True", "True" and "True". Regardless of whether you have an empty string, a string with 1 space, or a string with several spaces the IsNullOrWhiteSpace method is perfect for checking user input. Some users may try to enter a lot of spaces to get around a required field. Using this method can help you ensure that spaces will not get by your requirement for a field that has actual data in it.

No matter how long you have been working with the .NET framework, you can always find something new to learn about. I hope these couple of tricks will help you in your programming.

An Alternative to HTML Tables

I have long had a problem with using HTML tables to display data to the user. I have an even bigger problem with editing on a table, but that is a different discussion. An HTML table is easy to implement for a developer, and this is normally why developers use them. However, a table is not always the best method for conveying data to a user, especially when that data is most likely viewed on a mobile device. Of course, there are always exceptions to this rule, but these should be "the exception" and not the rule. There are many reasons why a table is not suitable for user consumption.

  • A table presents too much data on the screen so the user's eye has too much to try to concentrate on.
  • A user will get much more tired at the end of the day using a table as opposed to other display types.
  • The user cannot distinguish between the data in each column since each column is very uniform and nothing stands out.

The above are just some of the reasons why a table might not be the appropriate choice for displaying a list of data to the user. Look at Figures 1 and 3 for an example of a normal HTML table displayed in both a normal browser and on a mobile device. Then look at Figures 2 and 4 which should the alternative display of the same data on a desktop browser and a mobile device. As you can see the alternative design displays the data in a much easier-to-read format.

Using a HTML Table

A list of product data in a normal HTML table renders fine on a desktop browser, as illustrated in Figure 1. However, if you display that same product data on a mobile browser, you might only see the left most columns as illustrated in Figure 2. On some mobile browsers, they may render the table so small that it is hard to read. Either way, the user is forced to interact with their phone to view the data. They will either have to scroll left to right, or maybe pinch or spread with their fingers.


Figure 1: An HTML table rendered on a desktop browser.


Figure 2: An HTML table rendered on a mobile browser.

Using a Bootstrap Panel

Instead of using an HTML table, you might display a list of Bootstrap panel controls that contain your product data. In the sample shown in Figure 3 you see the same data, but the most important data, the product name, is displayed in the panel header area. The other information about the product is displayed within the body of the panel. The actions you can take are displayed within the panel footer area. As you can see this list of data looks just as good on a normal desktop browser (Figure 3) as on a mobile browser (Figure 4).


Figure 3: An alternative approach to a list of data rendered on a desktop browser.


Figure 4: An alternative approach to a list of data rendered on a mobile browser.

Create Mock Data Classes

Instead of messing with a database, create three simple classes to build a collection of Product objects that can be rendered on an MVC page. Figure 5 illustrates the properties and methods of each of the 3 classes. To view the code for these classes, see the instructions at the end of this blog post on how to download the sample.


Figure 5: A set of 3 product classes help us load mock product data for this sample

The MVC Controller

You need a MVC controller to load the data and feed that data to the CSHTML pages used to render our two pages of product data. Below is the code from the MVC controller. The TableSample and AlternateTableSample methods create an instance of the TrainingProductViewModel class and call the LoadProducts method to build the Products property in the view model. It is the collection property that is used to display the list of data in both pages.

public class HomeController : Controller
{
  public ActionResult Index() {
    return View();
  }
  public ActionResult TableSample() {
    TrainingProductViewModel vm = new TrainingProductViewModel();
    vm.LoadProducts();
    return View(vm);
  }
  public ActionResult AlternateTableSample() {
    TrainingProductViewModel vm = new TrainingProductViewModel();
    vm.LoadProducts();
    return View(vm);
  }
}

Create the HTML Table

The TableSample page is the one that renders the standard HTML table. Below is the code from the TableSample.cshtml page.

@model AlternativeTable.TrainingProductViewModel
@{
  ViewBag.Title = "Table Sample 1";
}
<h2>Table Sample 1</h2>
@using (Html.BeginForm()) {
  <div class="table-responsive">
    <table class="table table-bordered
                  table-condensed table-striped">
      <thead>
        <tr>
          <th>Product Name</th>
          <th>Introduction Date</th>
          <th class="text-right">Price</th>
          <th>URL</th>
          <th>Delete</th>
        </tr>
      </thead>
      <tbody>
        @foreach (TrainingProduct item in Model.Products) {
          <tr>
            <td>
              <a href="#"
                 title="Edit Product">
                @item.ProductName
              </a>
            </td>
            <td>@item.IntroductionDate.ToShortDateString()</td>
            <td class="text-right">@item.Price.ToString("c")</td>
            <td>@item.Url</td>
            <td>
              <a href="#"
                 title="Delete Product"
                 class="btn btn-sm btn-default">
                <i class="glyphicon glyphicon-trash"></i>
              </a>
            </td>
          </tr>
        }
      </tbody>
    </table>
  </div>
}

As you can see from the above code, there is nothing out of the ordinary for this table. You use the Bootstrap table classes to help with styling the table. You loop through the collection of product data in the Products property of the TrainingProductViewModel class. Each time through the loop, you display the appropriate data from the Product class in each <td> of the table.

Create the Alternative Design

The AlternateTableSample page uses the appropriate Bootstrap panel CSS classes to create the alternative design shown in Figures 2 and 4. Below is the code in the AlternateTableSample.cshtml page.

@model AlternativeTable.TrainingProductViewModel
@{
  ViewBag.Title = "Alt. Table Sample 1";
}
<h2>Alt. Table Sample 1</h2>
@foreach (TrainingProduct item in Model.Products) {
  <div class="panel panel-primary">
    <div class="panel-heading">
      <h1 class="panel-title">@item.ProductName</h1>
    </div>
    <div class="panel-body">
      <div class="row">
        <div class="col-xs-4 hidden-sm hidden-md hidden-lg">
          Intro Date
        </div>
        <div class="col-xs-4 col-md-3 hidden-xs">
          Introduction Date
        </div>
        <div class="col-xs-8 col-md-9">
          @item.IntroductionDate.ToShortDateString()
        </div>
      </div>
      <div class="row">
        <div class="col-xs-4 col-md-3">
          Price
        </div>
        <div class="col-xs-8 col-md-9">
          @item.Price.ToString("c")
        </div>
      </div>
      <div class="row">
        <div class="col-xs-4 col-md-3">
          URL
        </div>
        <div class="col-xs-8 col-md-9">
          @item.Url
        </div>
      </div>
    </div>
    <div class="panel-footer">
      <div class="row">
        <div class="col-xs-12">
          <a href="#"
             title="Edit Product"
             class="btn btn-sm btn-default">
            <i class="glyphicon glyphicon-edit"></i>
          </a>
          <a href="#"
             title="Delete Product"
             class="btn btn-sm btn-default">
            <i class="glyphicon glyphicon-trash"></i>
          </a>
        </div>
      </div>
    </div>
  </div>
}

Let's look at each section of the above code to see how it was put together. The first thing you see is the loop through the Model.Products collection. Within this loop is where you build a complete Bootstrap panel. In the heading area of the panel is where you place the ProductName property. Within the panel body is where you create a row and two columns for each of the other properties you wish to display from the Product object.

Notice that I am changing the column widths I use depending on the size of the browser. For anything that is a medium resolution and above, (according to Bootstrap) I am using a col-md-3 for the first column and col-md-9 for the second column. However as soon as a mobile device is detected, use the size of col-xs-4. If you look at the Introduction Date field you also notice that the words used are changed also depending on the size of the browser. This is one of the great things about using Bootstrap, the ability to hide and display things using simple CSS classes.

The last difference in this code compared to the normal HTML table is I use two glyphs for the actions that the user can take. It can be sometimes hard to click on a hyperlink with your finger on a mobile device. They can even be hard to see sometimes on a mobile device. I find using large buttons with a graphic gives the user a nice big target to hit with their finger.

Summary

In this blog post, you were presented with a method to present data to the user that is different from a traditional HTML table. While you may not like the exact user interface presented here, hopefully this article will spur you to question your user interfaces a little more and come up with some alternate methods of displaying lists of data.

Sample Code

You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose "PDSA Blog", then "An Alternative to HTML Tables" from the drop-down.


Keep Your Software Up-To-Date

Yes, it is always a struggle to keep your software up-to-date, but it is vital that you do so. Microsoft and Apple upgrade their operating systems every couple of years. Sometimes, when they do this, they break compatibility with their older operating system (OS). While this might be fine for most software, sometimes it does cause older software running on the new OS to break. If you have older software such as software written using Visual Basic 6 or earlier, FoxPro, PowerBuilder, and others, this means you are at risk of your software no longer working.

So, you might be thinking that you can just require your customers to run on the older operating systems. This might work for a couple of years, but at some point, your customers are going to want to, or need to, upgrade to the new OS. In fact, they may have to as Microsoft and Apple no longer support the older OS. There can be very serious consequences for not upgrading such as security risks. In today’s world with identity theft on the rise, people need to take every precaution to keep their computers safe. Staying current on the new OS versions is one way to stay safe.

You have a lot of time and money invested in your old software, and rewriting it can be an extensive undertaking. However, when you look at the alternative that your software no longer works, and you can no longer run your business, or your customers no longer purchase your software, how much is that worth? Could your business fail because of old software? This could be a real possibility.

So, the time to be looking at updating your software to new versions of .NET or Java is now, not when it stops working on a new OS release. By the time it stops working, you might not have enough time to rewrite before all your customers move on and you are now out of business.

Fairway Technologies can assist you with the migration of your old software to the most up-to-date languages and platforms. Call us today for a free consultation and let us help guide you on your path to new software that will last for years to come.

Use LINQ to XML Instead of a Database

When people think of having to store data for their applications, a database such as SQL Server immediately comes to mind. However, XML files are very handy for storing data without the overhead of a database. Using XML files to cache often-used, but seldom changed data such as US state codes, Country code, employee types and other validation tables can avoid network round-trips to a database, and potentially speed up your application. In addition, XML files are great for off-line applications where a user needs to add, edit and delete data when they can’t connect to a database.

To take advantage of XML within your application, you should use LINQ to XML. I am sure you have used LINQ to iterate over other types of collections. LINQ works great to iterate over XML, too. In this blog post you will use LINQ to XML to read state codes from an XML file and display those values on a web page.

The US State Codes XML File

An XML data file called USStates.xml is in the \Xml folder of the MVC project that you can download (see the instructions at the end of this post). This XML file contains several elements of US state code data. Each row has 3 elements as shown below

<USStates>

  <USState>
    <StateCode>AK</StateCode>
    <StateName>Alaska</StateName>
    <Capital>Juneau</Capital>
  </USState>
</USStates>

The USState Class

Just as you create an Entity class to map each column in a table to a property in a class, you should do the same for an XML file too. In this case you will create a USState class with properties for each of the elements within the parent element.

public class USState
{
  public string StateCode { get; set; }
  public string StateName { get; set; }
  public string Capital { get; set; }
}

The USStateManager Class

To read the US state codes from the XML file, make sure you have a reference to the System.Xml.Linq.dll in your project. Create a class called USStateManager. This manager class is going to have a method named GetStateCodes to which you pass in the full path and file name where the USStates.xml file is located in your MVC project. The full class listing is shown below. Take a look at the code, then we will discuss each piece.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace LinqToXmlSample
{
  public class USStateManager
  {
    public List<USState> GetStateCodes(string fileName) {
      // Load the XML file
      XElement xelem = XElement.Load(fileName);
      // Fill a list of USState objects
      var coll = from elem in xelem.Descendants("USState")
        select new USState
        {
          StateCode = GetValue<string>(elem.Element("StateCode"),
                                       ""),
          StateName = GetValue<string>(elem.Element("StateName"),
                                       ""),
          Capital = GetValue<string>(elem.Element("Capital"), "")
        };
      return coll.ToList();
    }
    private T GetValue<T>(XElement elem, T defaultValue) {
      if (elem == null || string.IsNullOrEmpty(elem.Value)) {
        return defaultValue;
      }
      else {
        return (T)Convert.ChangeType(elem.Value, typeof(T)); ;
      }
    }
  }
}

At the top of this file, you need a few using statements to support the various classes you use for reading in the XML and creating a list of USState objects. The GetStateCodes method accepts a fully qualified path and file name to the US state codes xml file. Using the XElement class, pass the file name to the Load() method. This method loads the XML file and returns an XElement object.

Once the XML file has been loaded, you write a LINQ query to iterate over the “USState” Descendants in the XML file. The “select” portion of the LINQ query creates a new USState object for each row in the XML file. You retrieve each element by passing each element name to the Element() method. This method returns an XElement object. This object has Value property from which you can retrieve the value, or a null value if the element does not exist.

Because you do not want to have a null value returned, a GetValue() method is written to accept an XElement object and a default value. This generic method checks to make sure that the XElement is not null, or that the value in the XElement object is not null or an empty string. If it is, then the default value you pass in is returned. If there is a value in the element, then that value is converted into the specified data type.

Calling the GetStateCodes Method

Now that you have written the code to retrieve the state codes and have them returned in a list of USState objects, let’s now call this method from a controller and display the state codes in a unordered list on an HTML page. Create a MVC controller named USStateController. Write the following code to create an instance of the USStateManager class, get the path and file name to the USStates.xml file, and call the GetStateCodes method.

public class USStateController : Controller
{
  public ActionResult Index() {
    USStateManager mgr = new USStateManager();
    string fileName = Server.MapPath("/Xml") + @"\USStates.xml";
    return View(mgr.GetStateCodes(fileName));
  }
}

Once you call the GetStateCodes method, the list of USState objects is passed directly to the CSHTML page via the View() method. The CSHTML page looks like the following. This page loops through each state code in the Model and builds a simple unordered list.

@model List<USState>
@{
  ViewBag.Title = "US State Codes";
}
<div class="row">
  <div class="col-md-12">
    <ul>
      @foreach (USState state in Model) {
        <li>
          @state.StateName (@state.StateCode)
        </li>
      }
    </ul>
  </div>
</div>

Summary

XML files can be a great alternative to storing data in a SQL Server table. LINQ to XML is a great way to retrieve data from those XML files, and if the files are stored on the web server, they can increase performance because you don’t have to go across the network to a database server. I have used LINQ to XML to work with XML files up to 10,000 rows of data and have seen a less than a second response times. I am sure you will find a lot of uses for XML in your applications.

Sample Code

You can download the complete sample code at my website. http://www.pdsa.com/downloads. Choose "PDSA Blog", then "Use LINQ to XML Instead of a Database" from the drop-down.


8 Questions to Ask to Determine if you should be in the Cloud

Everyone wonders if they should be moving to the cloud. Here are 8 starter questions you should ask yourself. Are you...


  1. Spending too much time on your IT infrastructure?
  2. Anticipating future growth?
  3. Needing to react quickly to change?
  4. Tired of hassling with Microsoft Exchange?
  5. Wanting to run your software as a service?
  6. Constantly adding more hardware to run your company
  7. Worried that your servers may not be backed up properly?
  8. Concerned about disaster recovery?

If you answered "Yes" to even one of these questions, a cloud solution may be just what you need. Companies are successfully using the cloud to scale their applications up and down without purchasing more equipment for the organization. Moving your applications into the cloud offers multiple benefits including:

  • Better security
  • Less manpower needed to manage software updates
  • More flexibility in application management
  • Disaster recovery is easier

If any of these keep you up at night, you should consider moving into the cloud. Fairway Technologies can help you migrate your infrastructure and your software to the cloud. Read more in our special report entitled Why Move to the Cloud?

Using MVVM in MVC Applications – Part 1

This blog post is the first in a series of four posts to discuss how to use a Model-View-View-Model (MVVM) approach in an MVC application. The MVVM approach has long been used in WPF applications, but has not been prevalent in MVC applications. Using a View Model class in MVC makes good sense as this blog post illustrates. You are going to be guided step-by-step building an MVC application using the Entity Framework and a View Model class to create a full CRUD web page.

Model-View-View-Model Approach

The reasons why programmers are adopting MVVM design pattern is the same reasons why programmers adopted Object Oriented Programming (OOP) over 30 years ago: reusability, maintainability and testability. Wrapping the logic of your application into classes allows you to reuse those classes in many different applications. Maintaining logic in classes allows you to fix any bugs in just one place and any other classes using that class automatically get the fix. When you don't use global variables in an application, testing your application becomes much simpler. Wrapping all variables and logic that operates upon those variables into one class allows you to create a set of tests to check each property and method in the class quickly and easily.

The thing to remember with MVVM is all you are doing is moving more of the logic out of the code behind of a user interface, or an MVC controller, and into a class that contains properties and methods you bind to the user interface. To use MVVM you must be using classes and not just writing all your code in a MVC controller. The whole key to MVVM or MVC is the use of classes with properties and methods that mimic the behavior you want in the UI. This means setting properties that are bound to UI controls and calling methods when you want to perform some action that you would normally write in a controller method.

Why Use MVVM in MVC

There are many reasons for using MVVM in an MVC application. Below are some of them.

1. Eliminate code in controller

a. Only two controller methods are needed

2. Simplify the controller logic

3. Can just unit test the view model and not the controller

4. View model classes can be reused in different types of projects (WPF, Web Forms, etc.)

Our Goal

This series of blog posts have a few goals for you to accomplish.

  1. Create a product table
  2. Create a set of MVC pages to list, search, add, edit, delete and validate product data
  3. Create a view model class to handle all these functions
  4. Write just two small methods in an MVC controller
  5. Learn ways to make more reusable and testable MVC applications

Create a Product Table

For this sample, create a table called Product in a SQL Server database. In the following code snippet, you see the full definition of the Product table.

CREATE TABLE Product (
  ProductId int NOT NULL 
                IDENTITY(1,1) 
                PRIMARY KEY NONCLUSTERED,
ProductName varchar(150) NOT NULL,
IntroductionDate datetime NOT NULL,
Url varchar(255) NOT NULL,
Price money NOT NULL
)

Add Data to the Table

Once you create the table, add some data to each field. Create enough rows that you can see several rows on your final HTML page. Below is the data that I used to create the list of product data for this sample application.


Figure 1: Product data

Here is the script to add the data to the Product table.

SET IDENTITY_INSERT Product ON 
GO
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (1, 'Extending Bootstrap with CSS, JavaScript and jQuery', '2015-06-11', 'http://bit.ly/1SNzc0i', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (2, 'Build your own Bootstrap Business Application Template in MVC', '2015-01-29', 'http://bit.ly/1I8ZqZg', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (3, 'Building Mobile Web Sites Using Web Forms, Bootstrap, and HTML5', '2014-08-28', 'http://bit.ly/1J2dcrj', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (4, 'How to Start and Run A Consulting Business', '2013-09-12', 'http://bit.ly/1L8kOwd', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (5, 'The Many Approaches to XML Processing in .NET Applications', '2013-07-22', 'http://bit.ly/1DBfUqd', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (6, 'WPF for the Business Programmer', '2009-06-12', 'http://bit.ly/1UF858z', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (7, 'WPF for the Visual Basic Programmer - Part 1', '2013-12-16', 'http://bit.ly/1uFxS7C', 29.0000);
INSERT Product (ProductId, ProductName, IntroductionDate, Url, Price) 
VALUES (8, 'WPF for the Visual Basic Programmer - Part 2', '2014-02-18', 'http://bit.ly/1MjQ9NG', 29.0000);
SET IDENTITY_INSERT Product OFF
GO

Create All Projects

As there are three pieces to a MVVM application, the Model, the View and the View Model, there are three Visual Studio projects you need to build within a single solution.

  1. Data Layer Class Library (Model)
  2. MVC Web Project (View)
  3. View Model Class Library (View Model)

Let's start building those now.

Create the Web Project

Open Visual Studio and click on the File | New Project to display the New Project window. Select Web from the Templates under Visual C#. From the list in the middle of the window select ASP.NET Web Application (.NET Framework). Set the name of this project to PTC as shown in Figure 2.


Figure 2: Create a new MVC Application named PTC.

Click the OK button. When prompted, choose the MVC template as shown in Figure 3 and click the OK button.


Figure 3: Choose the MVC template

Add Entity Framework

You are going to be using the Entity Framework in all threes projects. While you are still building this MVC project, let's go ahead and add the necessary DLLs and configuration file entries. Right mouse click on the project and select Manage NuGet Packages… Click on the Browse tab and search for Entity Framework. Click the Install button as shown in Figure 4.


Figure 4: Install the Entity Framework using NuGet

Create the Data Layer Project

Let's now build a project to just hold all data access classes and any other Entity classes we need to create. Right mouse click on the solution and choose Add | New Project. Select Windows from the Templates on the left tree view. Select Class Library from the middle part of the window. Set the name to PTC.DataLayer as shown in Figure 5.


Figure 5: Create a Class Library project for your Data Layer

Click the OK button to create the new project. Delete the file Class1.cs as you won't need this. Right mouse click on this project and select Add | New Item… From the New Item window select Data | ADO.NET Entity Data Model. Set the name to PTCData as shown in Figure 6. Click the Add button.


Figure 6: Add an ADO.NET Entity Data Model to the Data Layer project

On the Entity Data Model Wizard screen that is now displayed (Figure 7), select the Code First from database option.


Figure 7: Choose the Code First from database option.

Click the Next button to advance to the next screen. On the next page of this wizard (Figure 8) add a connection string where you created the Product table. Leave everything else as it is on this page and click the Next button.


Figure 8: Add a connection string to point to your database.

You are now going to choose the Product table you created earlier in this blog post. Drill down into your collection of tables and check the Product table as shown in Figure 9.


Figure 9: Select the Product table

Click the Finish button and Visual Studio will generate new classes that allow you to create, read, update and delete data in the Product table.

Create View Model Project

The last project you need to add is one for your View Model classes. Right mouse click on the solution and choose Add | New Project. Select Windows from the Templates on the left tree view. Select Class Library from the middle part of the window. Set the name to PTC.ViewModel as shown in Figure 10.

Click the OK button. Rename Class1.cs to ProductViewModel.cs.


Figure 10: Add a Class Library project for your View Models

Add Entity Framework

Just like you added the Entity Framework to the MVC project, you need to add it to this project as well. Right mouse click on the project and select Manage NuGet Packages… Click on the Browse tab and search for Entity Framework. Click the Install button as shown in Figure 11.


Figure 11: Add the Entity Framework to your View Model layer

Add References

Now that you have all the projects created, you need to add the appropriate references in each. Add a reference to the PTC.DataLayer and PTC.ViewModelLayer project from the PTC project. Add a reference to the PTC.DataLayer project from the PTC.ViewModelLayer project.

Add Connection String to PTC Project

Copy the <connectionString> element from the PTC.DataLayer app.config file to the PTC web.config file.

<connectionStrings>
  <add name="PTCData" 
       connectionString="YOUR CONNECT STRING HERE"
       providerName="System.Data.SqlClient" />
</connectionStrings>

At this point, your solution should look like Figure 12.


Figure 12: Your solution is now ready to start working within.

Retrieve Product Data

Let's start to write code in the ProductViewModel to retrieve a collection of Products using the Entity Framework generated code. Open the ProductViewModel class and add a couple of using statements:

using PTC.DataLayer;
using System.Collections.Specialized;

Add a property to the view model class to hold the collection of products.

public List<Product> DataCollection { get; set; }

Add another property to hold any messages to display to the user.

public string Message { get; set; }

Add an Init() method to initialize the DataCollection property and message property.

public void Init() {     
  // Initialize properties in this class
  DataCollection = new List<Product>();
  Message = string.Empty;
}

Add a constructor to call the Init() method

public ProductViewModel()
  : base() {
  Init();
}

Add a couple of methods to handle exceptions. You are not going to do any exception publishing in this blog post, but you want to have the methods there so you can add it easily later.

public void Publish(Exception ex, string message) {
Publish(ex, message, null);
}
public void Publish(Exception ex, string message,
                    NameValueCollection nvc) {
// Update view model properties
Message = message;
// TODO: Publish exception here
}

Add a method named BuildCollection() that calls the PTCData class to retrieve the list of products from the database table.

protected void BuildCollection() {
  PTCData db = null;
  try {
    db = new PTCData();
    // Get the collection
    DataCollection = db.Products.ToList();
  }
  catch (Exception ex) {
    Publish(ex, "Error while loading products.");
  }
}

HandleRequest Method

You are going to use the View Model class to handle many requests from your UI. Instead of exposing many different methods from the ProductViewModel class, let's create a single public method named HandleRequest(). For now, you are just going to call the BuildCollection() method from this method. However, later, you are going to add a switch…case statement to handle many different requests.

public void HandleRequest() {
  BuildCollection();
}

Create Product Controller

Now that you have the data layer and the view model classes created and ready to return data, you need a controller that can call our HandleRequest() method. Go back to the PTC project and right mouse click on the \Controllers folder. Select Add | Controller… from the menu. Choose the MVC 5 Controller – Empty template and click the Add method as shown in Figure 13.


Figure 13: Add an empty MVC contoller

When prompted to set the Controller name, type in ProductController and click the Add button as shown in Figure 14.


Figure 14: Create your Product controller

You will now see a controller class in your \Controllers folder that looks like the following.

public class ProductController : Controller
{
  // GET: Product
  public ActionResult Index() {
    return View();
  }
}

Add a using statement at the top of this class so you can use the ProductViewModel class in this controller.

using PTC.ViewModelLayer;

Modify the GET method to look like the following code.

public ActionResult Product() {
  ProductViewModel vm = new ProductViewModel();
  vm.HandleRequest();
  return View(vm);
}

List Products

Under the \Views folder see if you have a \Product folder already. If you don't, then add one. This folder is generally created when you add a controller called ProductController. Right mouse click on the \Views\Product folder and select the Add | MVC 5 View Page with Layout (Razor) from the menu as shown in Figure 15.


Figure 15: Add an MVC View with a shared layout page

When prompted to set the name for the item, type in Product as shown in Figure 16.


Figure 16: Create your Product MVC page

After setting the name you will be prompted to select the shared layout page. Select the \Views\Shared\_Layout.cshtml page as shown in Figure 17.


Figure 17: Select the _Layout.cshtml shared layout page

After the MVC page is added, add two statements as shown below.

@using PTC.ViewModelLayer
@model ProductViewModel

Write the code to display a table of product data. Add the following HTML after the code shown above.

<div class="table-responsive">
  <table class="table table-condensed table-bordered 
                table-striped table-hover">
    <thead>
      <tr>
        <th>Product Name</th>
        <th>Introduction Date</th>
        <th>Url</th>
        <th>Price</th>
      </tr>
    </thead>
    <tbody>
      @foreach (var item in Model.DataCollection) {
        <tr>
          <td>@item.ProductName</td>
          <td>
            @Convert.ToDateTime(item.IntroductionDate)
               .ToShortDateString()
          </td>
          <td>@item.Url</td>
          <td>
            @Convert.ToDecimal(item.Price).ToString("c")
          </td>
        </tr>
      }
    </tbody>
  </table>
</div>

Run the page and you should see a page that looks like the following.


Figure 18: You should now see some product data on your page

Summary

In this blog post, you created the start of an MVC application that is going to use a MVVM design pattern. You learned a few reasons why using an MVVM approach is a solid design decision in almost any kind of application. In the next blog post you learn to search for products and break up your single MVC page into a couple of different partial pages.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category "PDSA Blogs", then locate the sample Using MVVM in MVC Applications.

Using MVVM in MVC Applications – Part 4

This blog post continues from the last two blog posts that you can find here.

This post is going to finish the MVC application using a MVVM approach. You are going to build the methods to select a single product from the product table. You are going to learn to update an existing product. You are also going to delete a product. Finally you learn how to handle server-side validation, and return validation messages back to the client to display to the user.

Get a Single Product

Prior to updating a record, you should go back to the table and retrieve the most current information. To do this, you require the primary key of the product record. This means you need to create a new property in your ProductViewModel class to hold this data. You also need to put the primary key value into a data- attribute, then take that value and place it into a hidden field on your page so it can be bound to the view model.

Open ProductViewModel and add a new property named EventArgument. It is in this property you are going to place the primary key value.

public string EventArgument { get; set; }

Modify the Init() method to initialize this property.

EventArgument = string.Empty;

Open the _ProductList.cshtml page

Add a new column header before all the other column headers in the <thead> area.

<th>Edit</th>

Now add a new column within the @foreach() loop.

<td>
  <a href="#" 
     data-pdsa-action="edit"
     data-pdsa-arg="@item.ProductId"
     class="btn btn-default btn-sm">
    <i class="glyphicon glyphicon-edit"></i>
  </a>
</td>

Notice the new data-pdsa-arg attribute. This attribute's value is filled in with the primary key of the table. You need to store this value into the EventArgument property you just added to the view model class. Open the Product.cshtml page and add a new hidden field below the EventAction hidden field.

@Html.HiddenFor(m => m.EventArgument, new { data_val = "false" })

To add the product id to the EventArgument hidden field, open the pdsa-action.js script file and add the following line.

$("#EventArgument").val($(this).data("pdsa-arg"));

If you ran the application right now and clicked on one of the edit buttons you would see that the EventArgument property of the view model is filled in.

Write GetEntity Method

Before you update a product, it is a good idea to retrieve the complete record from the table to ensure you have the latest values. Write a GetEntity method to use the EventArgument and set the Entity property with the Product returned from the Find method of the Products collection.

protected virtual void GetEntity() {
  PTCData db = null;
  try {
    db = new PTCData();
    // Get the entity
    if (!string.IsNullOrEmpty(EventArgument)) {
      Entity = 
       db.Products.Find(Convert.ToInt32(EventArgument));
    }
  }
  catch (Exception ex) {
    Publish(ex, "Error Retrieving Product With ID=" 
                 + EventArgument);
  }
}

Now modify the "edit" case statement in the HandleRequest to call this new method.

case "edit":
  IsValid = true;
  PageMode = PDSAPageModeEnum.Edit;
  GetEntity();
  break;

Once the Entity property is set, you display the product detail page and the data from the item selected in the list is displayed in the fields. Run the page right now and verify that you are retrieving the correct product and displaying that product in the fields on the detail page.

Update the Product

Now that you have retrieved a product from the database and displayed it on the detail page, let's save changes back to the Product table. Add a using statement at the top of the ProductViewModel class. You need this namespace so you have access to the DbEntityValidationException class.

using System.Data.Entity;

Modify the Update method in the ProductViewModel class to look like the following:

protected void Update() {
  PTCData db = null;
  try {
    db = new PTCData();
    // Do editing here
    db.Entry(Entity).State = EntityState.Modified;
    db.SaveChanges();
    PageMode = PDSAPageModeEnum.List;
  }
  catch (DbEntityValidationException ex) {
    IsValid = false;
    ValidationErrorsToMessages(ex);
  }
  catch (Exception ex) {
    Publish(ex, "Error Updating Product With ID=" 
                 + Entity.ProductId.ToString());
  }
}

Run the product page and change some of the product data. Press the Save button and you should see the list refreshed with the changes you made to the one record.

Delete a Product

Now that you have added and updated a product, you should now allow the user to delete a product. To delete a product, add a button to the HTML table next to each product. Open the _ProductList.html page and add a new column header in the <thead> area.

<th>Delete</th>

Next, add a column as the last column within the @foreach() statement.

<td>
<a href="#" 
data-pdsa-action="delete"
data-pdsa-arg="@item.ProductId"
data-pdsa-deletelabel="Product"
class="btn btn-default btn-sm">
<i class="glyphicon glyphicon-trash"></i>
</a>
</td>

Again, you are going to use the data-pdsa-arg attribute to hold the primary key of the product record to delete. There is also one additional data- attribute called data-pdsa-deletelabel. This allows you to pass in the text to display when the JavaScript confirm() function is called to ask the user if they wish to "Delete this Product?". The value Product is what is replaced in the string. Modify the pdsa-action.js file to display a "Delete" message to the user.

$(document).ready(function () {
  // Connect to any elements that have 'data-pdsa-action'
  $("[data-pdsa-action]").on("click", function (e) {
    var deletelabel = '';
    var submit = true;
    e.preventDefault();
    // Check for Delete
    if ($(this).data("pdsa-action") === "delete") {
      deletelabel = $(this).data("pdsa-deletelabel");
      if (!deletelabel) {
        deletelabel = 'Record';
      }
      if (!confirm("Delete this " + deletelabel + "?")) {
        submit = false;
      }
    }
    // Fill in hidden fields with action 
    // and argument to post back to model
    $("#EventAction").val($(this).data("pdsa-action"));
    $("#EventArgument").val($(this).data("pdsa-arg"));
    if (submit) {
      // Submit form with hidden values filled in
      $("form").submit();
    }
  });
});

Open the ProductViewModel class and add a new method to delete a product.

public virtual void Delete() {
  PTCData db = null;
  try {
    db = new PTCData();
    if (!string.IsNullOrEmpty(EventArgument)) {
      Entity = 
       db.Products.Find(Convert.ToInt32(EventArgument));
      db.Products.Remove(Entity);
      db.SaveChanges();
      PageMode = PDSAPageModeEnum.List;
    }
  }
  catch (Exception ex) {
    Publish(ex, "Error Deleting Product With ID=" 
                 + Entity.ProductName);
  }
}

Add a new case statement in the HandleRequest for deleting a product.

case "delete":
  Delete();
  break;

Run the Product page, click on a product, answer OK when prompted, and you should see the product table refreshed with the product you deleted no longer displayed.

Add Server-Side Validation

Now that you have all the client-side validation working, add similar functionality to the server-side code as well. As it is very simple for a hacker to bypass client-side validation, you always check to ensure the data is validated on the server-side as well. To do this you add a new class to the DataLayer project named PTCData-Extension. After the file is added, rename the class inside of the file to PTCData and make it a partial class. This will allow us to add additional functionality to the PTCData Entity Framework model created in part two of this blog post series.

public partial class PTCData
{
}

When you attempt to insert or update data using the Entity Framework, it first calls a method named ValidateEntity to perform the validation on any data annotations added to each property. You may override this method to add your own custom validations. Add the following code to the PTCData class in the PTCData-Extension.cs file you just added.

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) {
  return base.ValidateEntity(entityEntry, items);
}

Add a new method named ValidateProduct just after the ValidateEntity method you added. In this method is where you add your own custom validations. You return a list of DbValidationError objects for each validation that fails.

protected List<DbValidationError> ValidateProduct(Product entity) {
  List<DbValidationError> list = new List<DbValidationError>();
  return list;
}

The ValidateEntity method is called once for each entity class in your model that you are trying to validate. In our example, you are only validating the Product object since that is what the user is inputting. The entityEntry parameter passed into this method has an Entity property which contains the current entity being validated. Write code to check to see if that property is a Product object. If it is, pass that object to the ValidateProduct method. The ValidateProduct method returns a list of additional DbValidationError objects that need to be returned. If the list count is greater than zero, then return a new DbEntityValidationResult object by passing in the entityEntry property and your new list of DbValidationError objects.

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) {
List<DbValidationError> list = new List<DbValidationError>();
if (entityEntry.Entity is Product) {
Product entity = entityEntry.Entity as Product;
list = ValidateProduct(entity);
if (list.Count > 0) {
return new DbEntityValidationResult(entityEntry, list);
}
}
return base.ValidateEntity(entityEntry, items);
}

Now write the ValidateProduct method to perform the various validations for your Product data. Check the same validations you performed on the client-side.

protected List<DbValidationError> ValidateProduct(Product entity) {
  List<DbValidationError> list = new List<DbValidationError>();
  // Check ProductName field
  if (string.IsNullOrEmpty(entity.ProductName)) {
    list.Add(new DbValidationError("ProductName",
        "Product Name must be filled in."));
  }
  else {
    if (entity.ProductName.ToLower() ==
        entity.ProductName) {
      list.Add(new DbValidationError("ProductName",
        "Product Name must not be all lower case."));
    }
    if (entity.ProductName.Length < 4 ||
        entity.ProductName.Length > 150) {
      list.Add(new DbValidationError("ProductName",
        "Product Name must have between 4 and 150 characters."));
    }
  }
  // Check IntroductionDate field
  if (entity.IntroductionDate < DateTime.Now.AddYears(-2)) {
    list.Add(new DbValidationError("IntroductionDate",
      "Introduction date must be within the last two years."));
  }
  // Check Price field
  if (entity.Price < Convert.ToDecimal(0.1) ||
      entity.Price > Convert.ToDecimal(9999.99)) {
    list.Add(new DbValidationError("Price",
      "Price must be between $0.1 and less than $9, 999.99."));
  }
  // Check Url field
  if (string.IsNullOrEmpty(entity.Url)) {
    list.Add(new DbValidationError("Url",
        "Url must be filled in."));
  }
  else {
    if (entity.Url.Length < 5 ||
        entity.Url.Length > 255) {
      list.Add(new DbValidationError("Url",
        "Url must have between 5 and 255 characters."));
    }
  }
  return list;
}

Run the Product page and try putting in some values that are outside the ranges specified in this method and watch the messages displayed.

Summary

In this series of blog posts you learned techniques for using the Model-View-View-Model design pattern in the context of an MVC application. There are many advantages to using MVVM in an MVC application as you saw. By using these techniques, you should find yourself writing more reusable code and code that can be tested much easier.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category "PDSA Blogs", then locate the sample Using MVVM in MVC Applications.

Using MVVM in MVC Applications – Part 2

This blog post continues from where the last blog post left off. You are going to learn to search for products. You also learn how to handle all post backs through a single method in your MVC controller. You will add code to check for no rows being returned, and display a message to the user. Finally you break up the single page into multiple partial pages.

Search for Products

You are now going to add a text box to allow the user to fill in a partial product name to search on. You are going to add two new buttons; one to allow the user to search on the product name they fill in, and one to clear the product name text box as shown in Figure 19.


Figure 19: Add a search area for your product page

To start out, you need a class to hold the search data. Currently there is only the one search field, Product Name, but you might add additional ones later. This means you only need to add one property to this new class.

Go to PTC.DataLayer project and add a new folder called \EntityClasses. Add a new class called ProductSearch under the \EntityClasses folder. Write the following code in this class.

public class ProductSearch
{
  public ProductSearch() : base() {
    Init();
  }
  public void Init() {
    // Initialize all search variables
    ProductName = string.Empty;
  }
  public string ProductName { get; set; }
}

Go to the PTC.ViewModelLayer project and open the ProductViewModel class. Add a using statement at the top of this class so you can use the new ProductSearch class.

using PTC.DataLayer.EntityClasses;

Add a new property to your view model class that you can use to bind to the user interface.

public ProductSearch SearchEntity { get; set; }

Add the following line of code to the Init() method

SearchEntity = new ProductSearch();

Locate the BuildCollection method and add the following code after you set the DataCollection to the results of the db.Products.ToList().

// Filter the collection
if (DataCollection != null && DataCollection.Count > 0) {
  if (!string.IsNullOrEmpty(SearchEntity.ProductName)) {
    DataCollection = DataCollection.FindAll(
      p => p.ProductName
        .StartsWith(SearchEntity.ProductName,
           StringComparison.InvariantCultureIgnoreCase));
  }
}

Hook up the Buttons

Many MVC developers add a separate controller method for each button and hyperlink they add to a page. This leads to a lot of methods in your controller. Instead, add a string property, named EventAction, in your ProductViewModel class that tells you which button or hyperlink was pressed. This string value is going to be set into the EventAction property via a tiny bit of jQuery code. Add the following property to the ProductViewModel class.

public string EventAction { get; set; }

Modify the Init() method to initialize this property.

EventAction = string.Empty;

Go back to the PTC web project, open the Product.cshtml page, and add a <form> tag around your HTML using the Html helper BeginForm() method.

@using (Html.BeginForm()) {
  // HTML Code you wrote before
}

Add a hidden control to bind to the EventAction property you created.

@using (Html.BeginForm()) {
  @Html.HiddenFor(m => m.EventAction, 
                  new { data_val = "false" })
  // HTML Code you wrote before
}

Build Search Input HTML

Add a panel to build the search area on the page. Add the following HTML below the hidden control and before the other HTML code you wrote earlier. Notice in this code you are binding to the ProductName property of the ProductSearch class you built earlier.

<div class="panel panel-primary">
  <div class="panel-heading">
    <h1 class="panel-title">Search for Products</h1>
  </div>
  <div class="panel-body">
    <div class="form-group">
      @Html.LabelFor(m => m.SearchEntity.ProductName,
                     "Product Name")
      @Html.TextBoxFor(m => m.SearchEntity.ProductName, 
                       new { @class = "form-control" })
    </div>
  </div>
  <div class="panel-footer">
    <button id="btnSearch"
            class="btn btn-sm btn-primary"
            data-pdsa-action="search">
      <i class="glyphicon glyphicon-search"></i>
      &nbsp;Search
    </button>
    <button id="btnReset"
            class="btn btn-sm btn-primary"
            data-pdsa-action="resetsearch">
      <i class="glyphicon glyphicon-share-alt"></i>
      &nbsp;Reset
    </button>
  </div>
</div>

Add JavaScript File for Retrieve Action

In the HTML you just wrote, notice the data-pdsa-action attributes are filled in with two string values; search and resetsearch. Each time one of the buttons is clicked on, you want to take the associated string value and put it into to the hidden field. You then submit the form to have it post back to the Product controller with the SearchEntity.ProductName value from the text box filled in, and the EventAction property filled in with either "search" or "resetsearch".

Right mouse click on the \scripts folder and select Add | JavaScript file. Set the name to pdsa-action.js. Write the following code within this file.

$(document).ready(function () {
  // Connect to any elements that have 'data-pdsa-action'
  $("[data-pdsa-action]").on("click", function (e) {
    e.preventDefault();
    // Fill in hidden field with action to post back to model
    $("#EventAction").val($(this).data("pdsa-action"));
    // Submit form with hidden values filled in
    $("form").submit();
  });
});

Go to the bottom of your Product.cshtml page and add a reference to this script file.

@section scripts {
  <script type="text/javascript"
          src="~/scripts/pdsa-action.js"></script>
}

Add Post Method in Controller

Now that you are ready to handle post backs from the user, you need to add an HttpPost method to your ProductController class. Open the ProductController class and add a new method that looks like the following.

[HttpPost]
public ActionResult Product(ProductViewModel vm) {
  // Handle action by user
  vm.HandleRequest();
  // Rebind controls
  ModelState.Clear();
      
  return View(vm);
}

Modify HandleRequest Method

Previously in the HandleRequest() method you had it calling the BuildCollection and nothing else. You now have a couple of new actions that can be performed. The user can choose to "search" or "resetsearch". You need to add a switch…case to handle these different event actions. Locate the HandleRequest() method in your ProductViewModel and modify it to look like the following.

public void HandleRequest() {
  // Make sure we have a valid event command
  EventAction = (EventAction == null ? "" :
                   EventAction.ToLower());
  switch (EventAction) {
    case "search":
      break;
    case "resetsearch":
      SearchEntity = new ProductSearch();
      break;
  }
  BuildCollection();
}

Run the application, type in "b" into the Product Name search box, click the Search button, and you should see just a list of products that start with the letter "b".

Inform User of No Rows

When the user searches and no rows are returned from that search, or if there are just no rows in the Product table, you should inform the user of this fact. You can display messages to the user using the Message property you added earlier to the ProductViewModel class. Modify the HandleRequest() method and add the following after the call to the BuildCollection() method.

if (DataCollection.Count == 0) {
  Message = "No Product Data Found.";
}

Open the Product.cshtml page and add the following lines of code around all the HTML that displays the table on this page. Don’t wrap it around the "search" area of the page.

@if (Model.DataCollection.Count > 0) {
  <div class="table-responsive">
  ...  // ALL THE OTHER HTML HERE
  </div>
}
else {
  <div class="row">
    <div class="col-xs-12">
      <div class="jumbotron">
        <h2>@Model.Message</h2>
      </div>
    </div>
  </div>
}

Run the Product page and enter a few random letters into the search text box. Click on the Search button and you should see the message displayed.

Use Partial Pages

Instead of putting all the HTML for this page on a single cshtml page, let’s break each area up into separate partial pages. Copy and paste the Product.cshtml page into the \Product folder. Rename the new file to _ProductList.cshtml. Leave the @model directive at the top of the file, and then just leave the logic for building the HTML table of product data.

Once again, copy and paste the Product.cshtml page into the \Product folder. Rename the new file to _ProductSearch.cshtml. Leave the @model directive at the top of the file, and leave everything that is associated with the search area. This is the HTML between the <div class="panel panel-primary"> and the </div> for that panel.

Open the Product.cshtml and modify this file to look like the following:

@using PTC.ViewModelLayer
@model ProductViewModel
@using (Html.BeginForm()) {
  @Html.HiddenFor(m => m.EventAction, 
                  new { data_val = "false" })
  @Html.Partial("_ProductSearch", Model)
  @Html.Partial("_ProductList", Model)
}
@section scripts {
  <script type="text/javascript" 
          src="~/scripts/pdsa-action.js"></script>
}

Run the application and everything should still be working.

Summary

In this blog post, you broke up the MVC page into two partial pages that are called from the main page. You learned how to handle all post backs with just a single post method in your MVC controller. You created additional code in your view model to handle searching for products. In the next blog post you add a detail page and learn to add products to the product table.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category "PDSA Blogs", then locate the sample Using MVVM in MVC Applications.

Using MVVM in MVC Applications – Part 3

This blog post continues from the last two blog posts that you can find here.

In this post you add a product detail page in order to gather product data for adding to the product table. You add a save and a cancel button and learn to display validation messages. You build a method in the view model class to insert product data.

Add a Product

To add or update a product, you need to create a detail page (Figure 21) with data entry fields for each field in the table. You need to be able to display validation errors if the user does not fill out correct data for the fields. You also need to hide the Search and List areas of the screen while the user is in add or edit mode.

Create an enumeration that can help you keep track of what "mode" the page is in. There are three different modes; List, Add, and Edit. So, you need three enumerated values. Add a file in your PTC.ViewModelLayer project called PDSAPageModeEnum.cs. Remove any code in this file and replace it with the following enum definition.

public enum PDSAPageModeEnum
{
List,
Add,
Edit
}

Open the ProductViewModel class and add a new property called PageMode that is of the type of this enumeration. Also, add a property called Entity, of the type Product, that you can use to bind to the fields on the detail page. One more property you need is a boolean called IsValid. This property informs the page to display any validation errors.

public PDSAPageModeEnum PageMode { get; set; }
public Product Entity { get; set; }
public bool IsValid { get; set; }

Modify the Init() method to initialize these three new properties.

PageMode = PDSAPageModeEnum.List;
Entity = new Product();
IsValid = true;

Modify the HandleRequest method and add two new case statements. You also need to set the PageMode to List within the other case statements.

public void HandleRequest() {
  // Make sure we have a valid event command
  EventAction = (EventAction == null ? "" :
                 EventAction.ToLower());
  Message = string.Empty;
  switch (EventAction) {
    case "add":
      IsValid = true;
      PageMode = PDSAPageModeEnum.Add;
      break;
    case "edit":
      IsValid = true;
      PageMode = PDSAPageModeEnum.Edit;
      break;
    case "search":
      PageMode = PDSAPageModeEnum.List;
      break;
    case "resetsearch":
      PageMode = PDSAPageModeEnum.List;
      SearchEntity = new ProductSearch();
      break;
  }
  if (PageMode == PDSAPageModeEnum.List) {
    BuildCollection();
    if (DataCollection.Count == 0) {
      Message = "No Product Data Found.";
    }
  }
}

You need a way for the user to go into Add or Edit mode. Start with the Add mode by adding an Add button to the search panel as shown in Figure 20.


Figure 20: An Add button allows you to navigate to a blank detail page

Open the _ProductSearch.cshtml file and locate the Reset button. Just below this button add an addition button for the Add.

<button id="btnAdd"
        class="btn btn-sm btn-primary"
        data-pdsa-action="add">
  <i class="glyphicon glyphicon-plus"></i>
  &nbsp;Add
</button>

With just this line of code above, you can run the application and if you were to set a breakpoint on the line in the HandleRequest method that sets the PageMode = PDSAPageModeEnum.Add; you would see that you stop on that line. This is a nice feature of using the data-pdsa-action attribute, you simply add a new data-pdsa-action="some value" and the HandleRequest method is called with the appropriate value set in the EventAction. You just need to add additional case statements to handle the new actions you wish to work with.

Build the Detail Page

It is now time to build the Product data entry page. Add a new partial page in the \Product folder named _ProductDetail.cshtml. Add the following HTML to create the page shown in Figure 21.

@model PTC.ViewModelLayer.ProductViewModel
<div class="panel panel-primary">
  <div class="panel-heading">
    <h1 class="panel-title">
      Product Information
    </h1>
  </div>
  <div class="panel-body">
    <!-- ** BEGIN MESSAGE AREA -->
    <div class="row">
      <div class="col-xs-12">
        @if (!Model.IsValid) {
          <div class="alert alert-danger
                      alert-dismissable"
               role="alert">
            <button type="button" class="close"
                    data-dismiss="alert">
              <span aria-hidden="true">
                &times;
              </span>
              <span class="sr-only">Close</span>
            </button>
            <!-- Model State Errors -->
            @Html.ValidationSummary(false)
          </div>
        }
      </div>
    </div>
    <!-- ** END MESSAGE AREA -->
    <!-- ** BEGIN INPUT AREA -->
    @Html.HiddenFor(m => m.Entity.ProductId)
    <div class="form-group">
      @Html.LabelFor(m => m.Entity.ProductName,
                 "Product Name")
      @Html.TextBoxFor(m => m.Entity.ProductName,
           new { @class = "form-control" })
    </div>
    <div class="form-group">
      @Html.LabelFor(m =>
       m.Entity.IntroductionDate,
       "Introduction Date")
      @Html.TextBoxFor(m =>
       m.Entity.IntroductionDate,
       new { @class = "form-control" })
    </div>
    <div class="form-group">
      @Html.LabelFor(m => m.Entity.Url, "Url")
      @Html.TextBoxFor(m => m.Entity.Url,
       new { @class = "form-control" })
    </div>
    <div class="form-group">
      @Html.LabelFor(m => m.Entity.Price, "Price")
      @Html.TextBoxFor(m => m.Entity.Price,
       new { @class = "form-control" })
    </div>
    <!-- ** END INPUT AREA -->
  </div>
  <div class="panel-footer">
    <div class="row">
      <div class="col-sm-12">
        <button id="btnSave"
                class="btn btn-sm btn-primary"
                data-pdsa-action="save">
          <i class="glyphicon
           glyphicon-floppy-disk"></i>
          &nbsp;Save
        </button>
        <button id="btnCancel"
                class="btn btn-sm btn-primary"
                formnovalidate="formnovalidate"
                data-pdsa-action="cancel">
          <i class="glyphicon
           glyphicon-remove-circle"></i>
          &nbsp;Cancel
        </button>
      </div>
    </div>
  </div>
</div>

Open the Product.cshtml page and modify the code in the @using (Html.BeginForm()) area. You are going to add a new hidden field to hold the current state of the PageMode property. You are also going to wrap an if statement around the rendering of the partial pages. You want to display the pages based on the mode of the view model.

@using (Html.BeginForm()) {
  @Html.HiddenFor(m => m.EventAction, 
                  new { data_val = "false" })
  @Html.HiddenFor(m => m.PageMode, 
                  new { data_val = "false" })
  if (Model.PageMode == PDSAPageModeEnum.List)
  {
    @Html.Partial("_ProductSearch", Model)
    @Html.Partial("_ProductList", Model)
  }
  if (Model.PageMode == PDSAPageModeEnum.Add ||
      Model.PageMode == PDSAPageModeEnum.Edit) {
    @Html.Partial("_ProductDetail", Model)
  }
}

You should be able to run the page, click on the Add button and you see the product detail page appear. You can’t click on the Save or Cancel buttons as you have not programmed those yet.


Figure 21: The detail page for inserting or updating a product

Cancel Button

First create the logic in your view model class to handle the user pressing the Cancel button on the detail page. Open the ProductViewModel class and add a new case statement as shown below.

case "cancel":
  PageMode = PDSAPageModeEnum.List;
  break;

Just these three lines of code will cause the page to go back into List mode, rebuild the collection of products and display the HTML table of products. Run the application, click on the Add button, then click on the Cancel button, and you should be able to cancel out of the detail screen.

Save Button

When the user clicks on the Save button, you are going to either Insert or Update the product data depending on how the user navigated to the detail page. You have only created the Add button so far, but later you will build the edit functionality. To prepare for both inserting and updating of product data, add some method stubs to your ProductViewModel class as shown below.

protected void Insert() {
}
protected void Update() {
}
protected void Save() {
  IsValid = true;
  if (PageMode == PDSAPageModeEnum.Add) {
    Insert();
  }
  else {
    Update();
  }
}

Locate the HandleRequest() method and add a new case statement to handle the "save" event action.

case "save":
  Save();
  break;

Validation Messages

When you attempt to insert or update, the user may not put in good information and thus some validation rules may fail. You need to report those errors back to the user. Earlier you added the @Html.ValidationSummary() method to generate any validation rule failures from Data Annotations generated by the Entity Framework. However, these will only display if JavaScript is turned on in the user’s browser. And, of course, hackers can bypass these rules easily.

You also need to test business rules on the server-side in case things are bypassed, and because we can write more rules that are not able to be added as Data Annotations at the time of generation. To handle these situations, you are going to add a property to your ProductViewModel class to hold a collection of string messages. This collection can be displayed on the _ProductDetail.cshtml page. 

At the top of the ProductViewModel class add a new using statement. This statement is needed as you are going to work with some objects from the Entity Framework namespace.

using System.Data.Entity.Validation;

Create a property named Messages to hold a collection of messages to display to the user.

public List<string> Messages { get; set; }

Modify the Init() method to initialize this new collection.

Messages = new List<string>();

Add a method to add the failures contained in a DbValidationException object to the Messages collection. This exception object is thrown by the Entity Framework if any validation rules fail. You will learn how a little later. For now, write the following method.

protected void ValidationErrorsToMessages(DbEntityValidationException ex) {
  foreach (DbEntityValidationResult result in ex.EntityValidationErrors) {
    foreach (DbValidationError item in result.ValidationErrors) {
      Messages.Add(item.ErrorMessage);
    }
  }
}

To display these messages, or any single message, open the _ProductDetail.cshtml page and right below the @Html.ValidationSummary(false) line add the following code.

@if (Model.Messages.Count > 0) {
<ul>
@foreach (string item in Model.Messages) {
<li>@item</li>
}
</ul>
}
else {
<span>@Html.Raw(Model.Message)</span>
}

Insert Method

It is now time to write the code to perform the insert into the Product table. Locate the Insert() method in your ProductViewModel class and add the following code.

protected void Insert() {
  PTCData db = null;
  try {
    db = new PTCData();
    // Do editing here
    db.Products.Add(Entity);
    db.SaveChanges();
    PageMode = PDSAPageModeEnum.List;
  }
  catch (DbEntityValidationException ex) {
    IsValid = false;
    ValidationErrorsToMessages(ex);
  }
  catch (Exception ex) {
    Publish(ex, "Error Inserting New Product: '"
                + Entity.ProductName + "'");
  }
}

When the SaveChanges() method is executed, validation errors could occur here. This is when the catch block that accepts a DbEntityValidationException would execute. You then take that exception object and pass it to the ValidationErrorsToMessages method to extract the message and put it into the Messages property. These messages would then be displayed in the message area of the detail page.

Modify the Post Method in the Product Controller

Now that you are going to handle any validation errors, you want to first check to see if the ModelState property is valid. This value is automatically checked by MVC for any Data Annotations that generated validation rules to be checked. Modify the POST method in the ProductController to look like the following.

[HttpPost]
public ActionResult Product(ProductViewModel vm) {
  vm.IsValid = ModelState.IsValid;
  if (ModelState.IsValid) {
    // Handle action by user
    vm.HandleRequest();
    // Rebind controls
    ModelState.Clear();
  }
  return View(vm);
}

Run the application and click on the Add button. Immediately click on the Save button without filling any information into the page, and you should see an error message appear. Now fill in some good product information and press the Save button. You should see the new information show up in the list.

Summary

In this post, you added a product detail page to gather product data from the user. You learned how to display validation messages if the data was not input correctly. In addition, you added an insert method to the view model class in order to add data to the product table. In the next post you write code to select a single product from the product table. You also learn to update and delete product data.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category "PDSA Blogs", then locate the sample Using MVVM in MVC Applications.

Add Angular to MVC – Part 4

If you have not already done so, you should go back and read the last three blog posts on how to integrate Angular 2 into an MVC application.

In this blog post, you will learn to retrieve a single product record using a Web API call from the Angular product service you created. You are going to add Edit and Delete buttons to each row of the HTML table (Figure 1) to allow the user to update and delete an existing product record. For this post, I am assuming you are a Microsoft Visual Studio developer and are familiar with MVC, Angular, C#, and the Web API.

Open up the project you created in the last blog post and follow along with the steps outlined in this one to create the final project.


Figure 1: Edit and delete buttons are added to each row in your product table

Get a Single Product

When the user clicks on the Edit button next to a specific product in the HTML table, you should go back to the server to retrieve the full product record for editing. This ensures you are getting the latest product data.

Add Get to Controller

To get a single product add a Get() method to your ProductApiController class. This Get() method is different from the other one in this class in that it accepts a product id of the product you wish to retrieve. Add this method, shown below to your ProductApiController class.

[HttpGet]
public IHttpActionResult Get(int id) {
  IHttpActionResult ret;
  ProductViewModel vm = new ProductViewModel();
  vm.GetProduct(id);
  if (vm.Entity != null) {
    ret = Ok(vm.Entity);
  }
  else {
    ret = NotFound();
  }
  return ret;
}

Add Get to Angular Product Service

Now that you have the Web API method created, write a getProduct() method in the ProductService component. Open the product.service.ts file and add the following method.

getProduct(id: number): Observable<Product> {
  let url = this.url + "/" + id;
  return this.http.get(url)
    .map(response => response.json() as Product)
    .catch(this.handleErrors);
}

This method builds a URL that looks like the following: api/productApi/2. The number 2 on the end is what gets passed to the id parameter in the Get() method in your ProductApiController.

Add Select Button to HTML Table

As you saw in Figure 1, you need to add an “Edit” column to your table. Open the product-list.component.html file and insert a new <td> element within the <thead> element.

<td>Edit</td>

Move down to the <tbody> element and insert a new <td> element in the same position.

<td>
  <button class="btn btn-default btn-sm"
          (click)="selectProduct(product.productId)">
    <i class="glyphicon glyphicon-edit"></i>
  </button>
</td>

Modify Product List Component

The click event on this button is going to call a method named selectProduct(). You pass in the product id to this method in order to pass this id to the detail page so it can load the product data associated with that id. Add the selectProduct() function to the ProductListComponent. This function is going to call the navigate function and pass that id to the product detail page.

selectProduct(id: number) {
  this.router.navigate(['/productDetail', id]);
}

Retrieve a Passed Parameter

You are going to need to modify the ngOnInit() method you previously wrote in the ProductDetailComponent. When you created the add functionality in the last blog post, you did not do anything with the parameter that was passed into the ProductDetailComponent. Now, since you are passing an id, you are going to use that id to call the getProduct method on the product service to retrieve the product. Open the product-detail.component.ts file and modify the ngOnInit() function to look like the following.

ngOnInit() {
  this.route.params.forEach((params: Params) => {
    if (params['id'] !== undefined) {
      if (params['id'] != "-1") {
        this.productService.getProduct(params['id'])
          .subscribe(product => this.product = product,
              errors => this.handleErrors(errors));
      }
      else {
        this.product = new Product();
        this.product.price = 1;
        this.product.url = "http://www.fairwaytech.com";
      }
    }
  });
}

In the above code you loop through the route.params array and retrieve a Params object. You check to see if the ‘id’ parameter is defined on that Params object. If the id value exists, check if that value to see if it is equal to a -1. If so, then assume you are adding a product. If the value is anything else, then it is a valid product id. Call the getProduct() method on the product service to retrieve a single product object.

At this point you can run the application and click on the Edit button. If you did everything correctly, you should see product data in all the input fields.

Update a Product

Now that you have the current product data displayed in the input fields, the user may update them. Create the appropriate code to perform the updating now.

Add PUT Method in Controller

Our first step, of course, is to add a new PUT method in the ProductApiController class. Open the ProductApiController.cs file and add the following code.

[HttpPut()]
public IHttpActionResult Put(int id, Product product) {
  IHttpActionResult ret = null;
  ProductViewModel vm = new ProductViewModel();
  if (product != null) {
    if (vm.Update(product)) {
      ret = Ok(vm.Entity);
    }
    else {
      if (vm.Messages.Count > 0) {
        ret = BadRequest(
               ConvertMessagesToModelState(vm.Messages));
      }
      else if (vm.LastException != null) {
        ret = InternalServerError(vm.LastException);
      }
    }
  }
  else {
    ret = NotFound();
  }
  return ret;
}

Add updateProduct to Product Service

Add another method to your Angular product service class to call this new PUT method. Open the product.service.ts file and add the following updateProduct() method.

updateProduct(product: Product): Observable<Product> {
  let headers = new Headers({ 'Content-Type': 
                              'application/json' });
  let options = new RequestOptions({ headers: headers });
  return this.http.put(this.url + "/" + product.productId,
                        product, options)
    .map(this.extractData)
    .catch(this.handleErrors);
}

When you PUT data, as opposed to getting data, you need to specify the content type as JSON data. You do this by creating a new Headers object and setting the ‘Content-Type’ property to ‘application/json’. Create a RequestOptions object and set the headers property to this new Headers object you created. Call the put method on the Http service passing in the product object and the RequestOptions object. When calling a PUT you specify the URL and you also add on the product id to that URL.

Modify the updateProduct() Method

In the last blog post you wrote an updateProduct method that was empty. It is now time to fill in this method. Open the product-detail.component.ts file and modify the updateProduct method to call the updateProduct method you created in the product service.

updateProduct(product: Product) {
  this.productService.updateProduct(product)
    .subscribe(() => this.goBack(),
      errors => this.handleErrors(errors));
}

Delete a Product

The last piece of functionality to add to your product page is the ability to delete product.

Add DELETE to Controller

Add a DELETE method to your ProductApiController to which you pass in the product id to delete. Open the ProductApiController.cs file and add the Delete method shown below.

[HttpDelete]
public IHttpActionResult Delete(int id) {
  IHttpActionResult ret;
  ProductViewModel vm = new ProductViewModel();
  if (vm.Delete(id)) {
    ret = Ok(vm.Entity);
  }
  else {
    ret = NotFound();
  }
  return ret;
}

Add deleteProduct to ProductService

Add a method to your Angular product service to call the delete method in your Web API. Open the product.service.ts file and add the deleteProduct() method shown below.

deleteProduct(id: number): Observable<Product> {
  return this.http.delete(this.url + "/" + id)
    .map(() => null)
    .catch(this.handleErrors);
}

Add Delete Button to HTML Table

As you saw in Figure 1, you need to add a “Delete” column to your table. Open the product-list.component.html file and insert a new <td> element within the <thead> element. Make this the last element in the <thead> element.

<td>Delete</td>

Add a <td> as the very last element in the <tbody> tag. Add a button with a click event that calls a method in your ProductListComponent class. Pass the current product id in the table to this method.

<td>
  <button class="btn btn-default btn-sm"
          (click)="deleteProduct(product.productId)">
    <i class="glyphicon glyphicon-trash"></i>
  </button>
</td>

Add deleteProduct() Method in List Component

Now that you have a button to call a deleteProduct method, go ahead and add that method. Open the product-list.component.ts file and add the code shown below.

deleteProduct(id: number) {
  if (confirm("Delete this product?")) {
    this.productService.deleteProduct(id)
      .subscribe(() => this.getProducts(),
      errors => this.handleErrors(errors));
  }
}

This method first confirms with the user that they really wish to delete this product. If they respond affirmatively, the deleteProduct() method on the Angular product service is called. If the delete is successful, the getProducts method is called to refresh the collection of products from the server and redisplay the list of products.

Summary

In this blog post you finished your product page by learning to retrieve, update and delete an existing product record. In these four blog posts you learned how to replace a single MVC page with an Angular page. There is no need to completely rewrite an MVC application. Instead, you can just replace a few pages that might need the performance of Angular.

Sample Code

You can download the code for this sample at www.pdsa.com/downloads. Choose the category “PDSA Blogs”, then locate the sample Add Angular to MVC – Part 4. NOTE: After downloading the sample, you will need to right mouse click on the package.json file and select the menu “Restore Packages”. You also need to create the Product table in a SQL Server database and update the connection string to point to your server and database name.