DPS907 WSA500 Lab 9

SOAP XML web service, and client app. Hosted on Azure.

Read/skim all of this document before you begin work.

Updated content, with some hints and helpful information, has been added, in blue text.

 

DPS907 WSA500 Lab 9 – Due Mon Dec 7

Due date: Monday, December 7, 2015, at 8:00am ET

Grade value: 10% of your final course grade

If you wish to submit the lab before the due date and time, you can do that.

Yes, your project can earn part marks if a feature is missing. 

 

Objective(s)

Create a legacy SOAP XML web service. Create a client that works with the web service.

 

Introduction to the problem to be solved

In the USA and Canada, Black Friday has captured the attention of many in our society. Therefore, we need a web service that models this curious aspect of pop culture.

The web service will be based on legacy SOAP XML technologies. It will enable a caller to create a sales order, among other tasks (e.g. get a list of available products, and display an existing sales order).

A client app, as a separate project, will interact with the web service.

Your work will be hosted publicly on Microsoft Azure Services.

Do you need to implement security?

No.

 

Specifications and work plan

The following specifications apply to all of your work:

  • Follows best practices
  • Implements the recommended system design guidance
  • Uses Entity Framework and Code First technology
  • Deployed to Microsoft Azure Services

For this assignment, here’s what we’re looking for:

  • Web service app, based on SOAP XML technology
  • Supports these operations:
    • Get a product list,
    • create a sales order, and
    • display a completed (saved) sales order
  • Client app, based on ASP.NET MVC web app technology
  • Supports these operations:
    • Create a sales order
    • List all sales orders
    • Display one completed (saved) sales order

The in-class grading on Monday, November 30, will look at these web service components:

  1. Entity classes
  2. Resource model classes
  3. Interface definition

 

Getting started

Create a new project, using the “Web API project v3” template, named Lab9Service.

Create a separate new project, using the familiar ASP.NET MVC web app template, named Lab9Client. The “Authentication” setting doesn’t matter.

 

Lab9Service design model classes

We will use a simple design model. The objectives of this assignment are to ensure that you can create and use a legacy SOAP XML web service, so this assignment is not robust or full-featured enough for real-world production use. Therefore:

We will have a simple Product class.

A separate class will hold a SalesOrder, which is related (to-many) with an OrderItem class. An OrderItem object has a required association with a SalesOrder object.

The design model looks like this:

design-model-classes

 

Lab9Service resource model classes

Next, create resource model classes. They must support these use cases:

  1. Get a collection of Product objects
  2. Get a collection of SalesOrder objects
  3. Get one SalesOrder object, with its associated collection of OrderItem objects
  4. Add new SalesOrder object, which includes a collection of OrderItem objects

 

Lab9Service – add a WCF Service

Add a WCF Service named “OrderService” to the project.

As you have learned, its interface defines the supported or expected operations.

In the interface source code file, add method declarations for the operations that will support the use cases. Pay attention to parameters and return types. Continue to follow the rules that you have learned: Accept and deliver resource model objects and collections. Do not leak information about the design model to callers.

 

Remember to test your work, incrementally

As you work on the remaining tasks, test your work frequently, using the WCF Test Client.

 

Complete the entity-related tasks

As you know, there’s a set of entity-related tasks that must be done after adding new entity classes to the app’s design model. Plan on completing them now, by following this list of reminders:

  1. Add DbSet property to the data context class for each entity
  2. Write/create the AutoMapper maps in the WebApiApplication class
  3. Add a new repository to manage each new entity (as required)
  4. Modify the Worker class, so that includes a property for each just-added repository class

 

Add some starter data for Products

Programmatically, add some starter data for the Product entity.

Ensure that this process runs only once. There are many ways of doing that. Ask if you need a reminder.

Think of something product-like in your life that you like. Create ten (10) products. Please set the initial value of the UnitsInStock property to a number in the hundreds, so that there are enough Product items available as you test your app’s logic.

 

Use case implementation –
Get a collection of Product objects

The service will deliver a collection of Product objects. Well, actually it will deliver a collection of resource model objects.

Like a controller, the service will call a repository method, which will actually do the work. Therefore, complete and test the code that enables the delivery of a collection of product objects.

Hint: While you are writing code in the product repository, add another method that will get one product by its identifier. This method will be needed later, when adding a new sales order, and validating the existence of a product. 

 

Use case implementation –
Get a collection of SalesOrder objects

The service will deliver a collection of SalesOrder objects, using a resource model class.

Should the resource model class include a collection of OrderItem objects? It doesn’t matter (for grading purposes). If your resource model does include the collection, then it can be used for this “get all” use case, and the “get one” use case that follows.

Then, you can still decide whether you fetch the associated OrderItem objects when you fetch the SalesOrder objects. If you do, they come along. If not, the collection’s value will be null for each item in the “get all” use case. That’s OK. Remember, we’re focusing on SOAP XML web service competence in this assignment, and we’re not trying to precisely model the real world with a production-ready app.

 

Use case implementation –
Get one SalesOrder object, with its associated collection of OrderItem objects

The service will deliver a specific SalesOrder object, using a resource model class.

Include its collection of OrderItem objects (obviously using a resource model class).

 

Use case implementation –
Add new SalesOrder object, which includes a collection of OrderItem objects

The service will accept a package of data, and create some objects in the data store.

What does the package of data look like? Well, think about any experience that you have had in a retail store. For example, Peter visits the Canadian Tire store, and selects two hammers and one screwdriver. That’s the data package – the sales order customer is “Peter”, and the two order items are hammers (quantity 2) and screwdriver (quantity 1). While processing the transaction, all the missing pieces of data are filled in (date, price, etc.).

This is not a complex task. Do NOT make it complex. It’s just arithmetic, that processes a simple, very common, and well-understood human activity. Create a coding plan, step-by-step, before you write code. Ask if you need help. (Duh, you’re not going to find a solution “out there” by using Google search.)

The result of this task will be to return a fully-configured sales order object (that includes a collection of order items).

Hint: As noted above, create a coding plan, step-by-step, before you write code. The following is the professor’s coding plan for his sample solution:

  1. Create a sales order object, based on the incoming data. When the data comes in, it will get mapped on to a SalesOrder object. Some properties will have data (Customer, OrderItems), but many properties will be empty, until they get filled in. Click the image to see the SalesOrder object at this point in time. sales-order-package-1
  2. Fill in the date-and-time value. Create an “order total” accumulator, which gets updated when processing each order item. 
  3. For each item in the “order items” collection…
    1. Look up the product, and ensure that it exists
    2. Update the product object, by adjusting the “units in stock” amount
    3. Update the “item total” value
    4. Make the association between the order item and the new sales order object
    5. Update the “order total” accumulator value
  4. After processing all the “order items”, update the sales order’s “order total” value
  5. Add the new sales order object to the entity collection, and save changes
  6. Return a mapped version of the sales order to the caller

 

Work on the client app

BEFORE starting the work on the client app, ensure that the web service works correctly. All four use cases. Use the WCF Test Client to ensure that it’s working.

Then, you can begin work on the client app.

 

Add service reference

One of the first tasks is to add a service reference to the project.

Names are important. The “Namespace” name will be used in your code to enclose the generated proxy classes, and enable access to the service class. Therefore, make it meaningful and clear.

In the “Add Service Reference” dialog, open the Advanced configuration dialog, and clear (un-check) the “Reuse types in referenced assemblies” checkbox.

Visual Studio will generate some code. It creates classes that act as a local proxy to the remote web service.

It also creates classes for the objects that will get passed to and from the web service. We can use these classes directly, or you can create your own resource model classes. (For this assignment, it is acceptable to use the classes directly, and consider them equivalent to resource model classes.)

 

Add the service layer code

As you have learned when creating ASP.NET MVC web apps, you should create a Manager class (in the Controllers folder). The Manager code will know how to contact the web service, and handle the request and response messages.

In a method that calls the web service, the typical pattern will:

  1. Create an instance of the web service client class
  2. Call a method (while paying attention to arguments and return types)

 

Add a controller and the necessary views

In the client app, add a standard controller, probably named SalesOrdersController.cs. The use cases are dominated by sales order processing, so make a decision about what kind of methods you need. You probably need these:

  1. Display a list of sales orders
  2. Display the details for a single/selected sales order
  3. A pair of methods to handle a typical “add new” sales order scenario

Keep it simple. Use view scaffolding when possible. In the views, ensure that Bootstrap classes are used for all HTML Form elements.

Hint: Are you looking for some help with the “add new” sales order task? 

Look at the Appendix to this Lab 9 specification, below. 

 

Making changes in the web service

If you make a change to the web service’s methods or resource models, the changes affect how client apps can use the web service.

Therefore, if you do change the web service, you must “update” the service reference in the client app. Locate the service references folder in Solution Explorer, right-click the service, and choose “Update Service Reference”.

 

Deploy your work to Microsoft Azure Services

Having problems deploying to or running on Azure? Check this document.

 

Preview of the deployment process

At a minimum, your Lab9Service will be deployed to Azure.

Then, in your client app, you must update the service reference, by pointing to the Azure-deployed web service. Then, test your client app again, to ensure that it still works.

If that works well, then you can deploy your client app to Azure.

 

Azure web app and database preparation for your Lab 9 service

Create a web app, with a database, for your Lab 9 service. The suggested name is (substitute your Seneca account name for the “astudent” placeholder below):

astudent-ws2015-lab9.azurewebsites.net

Deploy your Lab 9 service to this endpoint. Before continuing, use the WCF Test Client to interact with the service.

 

Update your client app

As noted above, update the service reference, now that the web service is on Azure. Right-click the service reference, and choose “Configure Service Reference”. Notice the localhost address:

configure-service-reference-before

 

Change the address to the Azure-deployed address:

configure-service-reference-after

 

Test the client app, to ensure that it works with the Azure-deployed web service.

If it does…

 

Azure web app (no database) for your Lab 9 client app

Create a web app, with no database, for your Lab 9 client app. The suggested name is (substitute your Seneca account name for the “astudent” placeholder below):

astudent-ws2015-lab9client.azurewebsites.net

You can now deploy your Lab 9 client app to this endpoint.

 

Testing your work

Use the WCF Test Client, and your client app.

 

Reminder about academic honesty

You must comply with the College’s academic honesty policy. Although you may interact and collaborate with others, you must submit your own work.

 

Submitting your work

At this point in time, you are familiar with the submission process:

  1. Copy both projects – Lab9Service, and Lab9Client
  2. Remove the packages, bin, and obj folders
  3. Zip and upload to the designated location on My.Seneca/Blackboard, before the due date

If you need more details, look at the info in assignments 1 through 4.

 

Appendix – Elegantly handling the “add new” sales order task

It would be nice to create an HTML Form that was easy and safe to use.

If you need help with this task, your professor can help by providing some guidance from his sample solution.

 

Requirement – add some new view models

We start with some view model classes. We need:

  • SalesOrder – represents the data posted by the HTML Form
  • OrderItem – represents one order item; a collection of these is in a sales order
  • SalesOrderAddForm – packaging for rendering the HTML Form

 

public class SalesOrder
{
    public string Customer { get; set; }
    public ICollection<OrderItem> OrderItems { get; set; }
}

public class OrderItem
{
    public int ProductId { get; set; }
    public int Quantity { get; set; }
}

public class SalesOrderAddForm
{
    public string Customer { get; set; }
    public ICollection<Lab9Client.HomeHardware.ProductBase> Products { get; set; }
}

 

Controller method to render the “add new” sales order view

This method does the following:

  1. Creates a new sales order add form
  2. Fetches the list of products (from the web service), and configures it in the sales order add form
  3. Finally, it passes the form to the view

 

“Create new sales order” view

Create the view to support the “add new” task. Use scaffolding, and specify the sales order add form class, so that it generates the view code. 

At this point in time, it will add code only for the “Customer” property. Remember (from your web apps programming course experience) that collections are NOT automatically code-generated. We must do that part ourselves. 

You can add the following view code, between the “Customer” text box, and the “Create” (submit) button:

 

<div class="form-group">
  @Html.LabelFor(model => model.Products, htmlAttributes: new { @class = "control-label col-md-2" })
  <div class="col-md-10">
    <table class="table">
      <tr>
        <th>OrderQuantity</th>
        <th>Name</th>
        <th>SellPrice</th>
        <th>UnitsInStock</th>
      </tr>
      @foreach (var item in Model.Products)
      {
        var name = "item" + item.Id.ToString();
        <tr>
          <td>
            <input class="form-control" type="number" min="0" max="10" value="0" name="@name" />
          </td>
          <td>@item.Name</td>
          <td>$ @item.SellPrice</td>
          <td>@item.UnitsInStock</td>
        </tr>
      }
    </table>
  </div>
</div>

 

Controller method to handle the HTML Form posted data

Normally, we would use a view model, and model state validation to process the incoming data.

For this situation, we will NOT do that.

We will process the incoming data as an untyped collection of form data items.

Therefore, the method argument type is FormCollection, which is a collection of key-value pairs.

Here’s the code from your professor’s sample solution:

 

// Handle HTML Form post
[HttpPost]
public ActionResult CreateOrder(FormCollection formData)
{
    // Notice that we are processing the incoming data
    // as an untyped collection of form data items

    // Ensure that we can continue
    if (string.IsNullOrEmpty(formData["Customer"]))
    {
        return RedirectToAction("CreateOrder");
    }

    // Start processing the order items...

    // Create a collection to hold the order items
    var orderItems = new List<OrderItem>();

    // Look at the entire collection of posted data items
    foreach (var item in formData.AllKeys)
    {
        // Process only the key-value pairs that begin with "item"
        if (item.StartsWith("item"))
        {
            // Get the product identifier, which is part of the key name
            var productIdString = item.Replace("item", "");

            // Extract and convert the values that we need
            // We'll use the unsafe convert here, because it will not fail
            var productId = Convert.ToInt32(productIdString);
            var quantity = Convert.ToInt32(formData[item]);

            // Make a new order item
            if (quantity > 0)
            {
                orderItems.Add(new OrderItem
               { ProductId = productId, Quantity = quantity });
            }
        }
    }

    // Ensure that we can continue
    if (orderItems.Count == 0)
    {
        return RedirectToAction("CreateOrder");
    }

    // Create a new sales order object
    var salesOrder = new SalesOrder
    { Customer = formData["Customer"], OrderItems = orderItems };

    // Pass it along for processing
    var addedItem = m.AddSalesOrder(salesOrder);

    return RedirectToAction("OrderDetails", new { id = addedItem.Id });
}

 

Passing the sales order object to the Manager method

As shown above, we created a custom view model, “SalesOrder”. 

It will not be automatically handled by the web service “add sales order” method, because the web service is expecting a “SalesOrderAdd” object. 

Therefore, in the Manager method, you must map the local SalesOrder object to a web service SalesOrderAdd object.

Use AutoMapper. Create maps for both the sales order, and the order item. 

 

Screen capture examples

Click on each to open it full-size in its own tab/window.

List of products:

client-products-list

 

List of sales orders:

client-sales-order-list

 

Add new sales order:

client-sales-order-add

 

Sales order detail:

client-sales-order-detail

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

%d bloggers like this: