Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

Archive for ASP.NET

October 15, 2013

ASP.NET Web API w/ASP.NET Client & Supporting IE 8

Filed under: ASP.NET,ASP.NET Web API @ 4:35 pm

Last week, I deployed (to test) my first "real" ASP.NET Web API service with a JQuery / ASP.NET client. The deployment ran into some minor difficulties, but no major issues. At least not until the testers decided to run it with IE 8. Then it was one thing after another after another.

I thought it would be useful to document all of the issues that we ran into for future reference.

Subdirectory

The very first problem that we ran into was the web site subdirectory. The existing site was deployed under a subdirectory, so it was accessed something like this:

http://<siteName>/OnlinePayment/CustomerPayment.aspx

Where "OnlinePayment" was the subdirectory name.

When running from localhost, there really is no concept of a subdirectory. So "it worked fine from my machine". But when the testers tried the first version of the test deployment, the Web API call kept returning "Not Found".

If I added the subdirectory to the path on the Web API call, then I could no longer run the application from localhost.

I ended up adding code something like that shown below in order to get the code to run either from localhost or from the site with a subdirectory:

var baseUrl = ‘http://’ + $(location).attr(‘host’);
var apiUrl;

($(location).attr(‘href’).toLowerCase().indexOf(‘onlinepayment’) > 0) {
        baseUrl += ‘/OnlinePayment’
}
apiUrl = baseUrl + ‘/API/Customer/';

This code retrieves the current URL. If the current URL contains the subdirectory name, then it appends the subdirectory name to the url used in the Web API call.

Domain Services

After getting the subdirectory code in place, our next error was "Internal Error". We were able to use the F12 tools to determine more specifically what the "internal error" was. It turned out that the code needed the domain services dlls!

System.ServiceModel.DomainServices.*

I assume this is because our business objects are shared with our Silverlight project which uses Domain Services.

I had to set a reference to the two files so they would be included with the deployment.

That did it! We finally have our page loading on the test server!

NOTE: As soon as we included these domain service DLLs, our deployment suddenly included separate folders for each language … really slowing down our deployment process.

‘JSON’ is undefined

Then it was time to deal with IE8. The page would not even load with the tester’s IE 8. Bummer!

On IE 10 (and any other browser/device they tried) the page worked. But not in IE 8.

The error was "’JSON’ is undefined. jquery-2.0.3.js Line: 349.

I found this link:

http://stackoverflow.com/questions/5339232/json-is-undefined-error-in-javascript-in-internet-explorer

So added the following:

<script src="Scripts/jquery-2.0.3.js"></script>
<script src="Scripts/jquery.validate.js"></script>
<script src="Scripts/json2.min.js"></script>

‘JSON’ is undefined

Well, that still didn’t work. Seems I needed them in order?

<script src="Scripts/json2.min.js"></script>
<script src="Scripts/jquery-2.0.3.js"></script>
<script src="Scripts/jquery.validate.js"></script>

OK, that got us a little further.

Object does not support this property or method

But then IE 8 displayed "Object does not support this property or method" line 834.

Alas, why didn’t I know (or maybe nuget could have told me?) that jQuery 2+ does not support IE 6, 7 or 8?

So spent a bit of time uninstalling the nuget packages I had for jQuery. Then I found out that I could not install an older version from nuget. That seemed odd.

I found Tools | Library Package Manager | Product Manager Console. This allowed me to use a command line within Visual Studio to install an older version of jQuery (1.10.2):

>Install-Package jQuery –Version 1.10.2

And we got the page to come up!

$(location).attr(‘href’)

But as jquery code was executed, IE 8 threw another error.

Remember this line from the code above that we added to handle the subdirectory?

$(location).attr(‘href’)

Well IE 8 does not like that code. I had to change it to the non-JQuery version:

window.location.pathname

Document Mode

Still … no go on IE 8. This time it is a problem with the validation … I’m using jquery-validate.

One of these days I need to set up some VM’s so I have a test system with IE 8. In the mean time, with the help of some very smart MVPs, I found that I could use the F12 tools to emulate IE 8 with IE 10. If I set IE 8 Quirks, I could see the same error that the testers were seeing. If I set to IE 8 Standard, the page would come up.

So I  added the following line at the top of the page. The page would then default to standards mode:

<!doctype html>
<html>
<head>

That was better! The validation would work, even on IE 8.

Accessing an Element

The page worked in all cases, except on IE 8 when there was a message returned from the service. The message would not appear on the page in IE 8.

The jquery code was simple:

$(‘#paymentAmountMessage’).text(returnData.CurrentOperationResult.Messages);

And the HTML:

<td style="max-width: 160px; text-wrap: normal;>
    <div>
        <label id="paymentAmountValidationMessage" class="validationMessage" />
    </div>
    <div>
        <label id="paymentAmountMessage" class="validationMessage" />
    </div>
</td>

After a SIGNIFICANT amount of work … it turned out to be nothing wrong with the service or the message or the jQuery. In IE 8 *only*, it could not find the paymentAmountMessage element.

I changed the HTML as follows:

<tr>
    <td style="max-width: 160px; text-wrap: normal;>
        <label id="paymentAmountValidationMessage" class="validationMessage" />
    </td>
</tr>
<tr>
    <td style="max-width: 160px; text-wrap: normal;">
        <label id="paymentAmountMessage" class="validationMessage" />
    </td>
</tr>

By replacing the two div tags with two separate rows, the code now appears to work in IE8.

I would add my standard "Enjoy" here at the end. But this was not enjoyable at all. Smile

Hope you have had better luck!

September 28, 2012

Web API Help Page and XML Comments

Filed under: ASP.NET,ASP.NET Web API,C#,VB.NET @ 1:32 am

The virtues of a Web API Help Page were extolled in a prior post. That prior post demonstrated the "out of the box" functionality of the Microsoft ASP.NET Web API Help Page. This post extends that functionality to include the XML comments from your code.

You can adjust the configuration file for the Help Page to set the documentation provider to your code’s XML comments. That way your help file documentation will match the XML comments from your code.

Let’s work through an example:

[See this prior post for an introduction to building an ASP.NET Web API service. The service created in that post is the example service used here.]

[[NOTE: The ASP.NET Web API Help Page is in prerelease and does not currently work with VB.NET.]]

Here is the prior example Web API service with XML Comments added:

In C#:

using ACMService.Models;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace ACMService.Controllers
{
    /// <summary>
    /// Provides customer information.
    /// </summary>
    public class CustomerController : ApiController
    {
        /// <summary>
        /// Provides information for the full list of customers.
        /// </summary>
        /// <returns></returns>
        /// <example>GET api/customer</example>
        public IEnumerable<Customer> Get()
        {
            return Customer.Retrieve();
        }

        /// <summary>
        /// Provides information for a single customer by Id.
        /// </summary>
        /// <param name="id">Customer Id</param>
        /// <returns></returns>
        /// <example>GET api/customer/2</example>
        public Customer Get(int id)
        {
            List<Customer> customerList = Customer.Retrieve();
            Customer customerInstance = customerList.FirstOrDefault(
                                    c => c.CustomerId == id);
            if (customerInstance == null)
            {
                throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound)
                    {
                        Content = new StringContent("Could not locate a customer with id: " + id),
                        ReasonPhrase = "Customer Id not found"
                    });
            }
            return customerInstance;
        }
    }
}

In VB:

Imports System.Net
Imports System.Net.Http
Imports System.Web.Http

”’ <summary>
”’ Privides customer information.
”’ </summary>
”’ <remarks></remarks>
Public Class CustomerVBController
    Inherits ApiController

    ”’ <summary>
    ”’ Provides information for the full list of customers.
    ”’ </summary>
    ”’ <returns></returns>
    ”’ <example>GET api/customerVB</example>
    Public Function [Get]() As IEnumerable(Of CustomerVB)
        Return CustomerVB.Retrieve()
    End Function

    ”’ <summary>
    ”’ Provides information for a single customer by Id.
    ”’ </summary>
    ”’ <param name="id">Customer Id</param>
    ”’ <returns></returns>
    ”’ <example>GET api/customerVB/2</example>
    Public Function [Get](id As Integer) As CustomerVB
        Dim customerList = CustomerVB.Retrieve()
        Dim customerInstance = customerList.FirstOrDefault(
                    Function(c) c.CustomerId = id)
        If customerInstance Is Nothing Then
            Throw New HttpResponseException(New HttpResponseMessage(HttpStatusCode.NotFound) With
            {
              .Content = New StringContent("Could not locate a customer with id: " & id),
                    .ReasonPhrase = "Customer Id not found"
            })
        End If
        Return customerInstance
    End Function
End Class

To modify the Help Pages to use the XML comments from the above code, follow these steps:

1) Ensure that your code has XML comments. (See the examples above.)

2) Modify the HelpPageConfig file.

Find it in Solution Explorer under your Web API project, Areas / HelpPage / App_Start folder.

3) Uncomment the code line provided in the HelpPageConfig file to set the documentation provider to the XML document generated from your Web API code.

image

4) Ensure that your build generates the required XML file.

image

5) Ensure that the XML file name used in the dialog above matches the XML file name defined in the HelpPageConfile file:

config.SetDocumentationProvider(new XmlDocumentationProvider
      (HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));

6) Execute the Web API project in Visual Studio and append "/help" to the address bar of the browser.

The result is shown below:

image

Notice that the description is now taken from your XML comments.

Drill down to see the detail:

image

Use these extra steps any time you want to use your Web API XML comments in your help pages.

Enjoy!

Discovering your Web API

Filed under: ASP.NET,ASP.NET Web API,C#,VB.NET @ 12:47 am

If your ASP.NET Web API service is on the ‘net and no one is around to explain it, does it provide a service? Unlike no one hearing the tree falling in the woods, this question is more than a philosophical thought experiment. It could be a big issue for the clients needing your service.

[See this prior post for an introduction to building an ASP.NET Web API service. The service created in that post is the example service used here.]

Once you have a Web API service in place, clients can call it. But there is no easy way for those clients to discover the available service methods or their parameters. What they need is a way to view your API.

That’s where the Help Page comes in to help you help them. As it states in the dialog below, "The ASP.NET Web API Help Page automatically generates help page content for the Web APIs on your site."

image

The Help Page is available as a NuGet Package. To install it:

1) Select your Web API service project in Solution Explorer.

2) Select Project | Manage NuGet Packages from the Visual Studio menu.

3) Search for "helppage" using the search box in the upper right corner of the resulting dialog.

4) Click the Install button next to the Microsoft ASP.NET Web API Help Page.

This package adds an Areas directory into the project which contains all of the generated code:

image

That’s it. After you install it you don’t have to do anything else.

To view the generated help page, launch your Web API service in Visual Studio and append "/help" to the url in the Address bar of the browser. The generated help page is displayed.

image

Notice that it contains the name of the controller and descriptions of the two methods in the Web API service.

Click on an action name to drill down. The details provide information on the parameters, if any, and the response body formats. It even contains an example response.

image

Since all of the code for these help pages are generated into your project, you can easily change them. You can style the layout and change the textual content as desired.

Use the Help Page NuGet package any time you want to provide documentation to the clients of your Web API service.

Enjoy!

September 25, 2012

Adding Simple Exception Handling to your Web API Service

Filed under: ASP.NET,ASP.NET Web API,C#,VB.NET @ 1:47 am

There is nothing perfect in life, not even an ASP.NET Web API service. The service may not understand the provided parameters, or may not find the requested data, or may have trouble connecting to the database. Whatever the issue, if the service cannot return the desired data it would be nice to instead return an appropriate exception notification.

[See this prior post for an introduction to building an ASP.NET Web API service. The service created in that post is the example service used here.]

The following code is the ASP.NET Web API service controller from the example mentioned above, but with a simple exception.

In C#:

using ACMService.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace ACMService.Controllers
{
    public class CustomerController : ApiController
    {
        // GET api/customer
        public IEnumerable<Customer> Get()
        {
            return Customer.Retrieve();
        }

        // GET api/customer/5
        public Customer Get(int id)
        {
            List<Customer> customerList = Customer.Retrieve();
            Customer customerInstance = customerList.FirstOrDefault(
                                    c => c.CustomerId == id);
            if (customerInstance == null)
            {
                throw new ApplicationException("Customer not found");
            }

            return customerInstance;
        }
    }
}

In VB:

Imports System.Net
Imports System.Net.Http
Imports System.Web.Http

Public Class CustomerVBController
    Inherits ApiController

    ‘ GET api/customerVB
    Public Function [Get]() As IEnumerable(Of CustomerVB)
        Return CustomerVB.Retrieve()
    End Function

    ‘ GET api/customerVB/2
    Public Function [Get](id As Integer) As CustomerVB
        Dim customerList = CustomerVB.Retrieve()
        Dim customerInstance = customerList.FirstOrDefault(
                    Function(c) c.CustomerId = id)
        If customerInstance Is Nothing Then
            Throw New ApplicationException("Customer not found")
        End If
        Return customerInstance
    End Function
End Class

Run the application. Press F12 to launch the F12 developer tools. Select the Network tab and the Start capturing button. [For more information on the F12 tools, see this prior post.]

Then add to the address bar the following: api/customer/10 (api/customerVB/10 in the VB example).

image

The F12 window displays the request with a result of 500: Internal Server Error. When an exception is thrown in a Web API controller, by default the response is returned with a status code of 500.

image 

Select the request and click the Go to detailed view button to see the details. Click on the Response body tab to see the error message as a JSON string.

image

Notice that it provides the full stack trace. This can be helpful when debugging the application, but may provide more information than you want to share when your service goes live.

As an alternative, you could use the provided HttpResponseException. By default, this has a header but no content. And instead of always returning a 500 status code, it returns the code you specify.

Change the exception thrown in the example above to instead throw a new HttpResponseException.

In C#:

throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));

In VB:

throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound))

Repeat the steps above to look at the result with the F12 developer tool:

image

Notice that the Result is now a 404: Not found. Looking at the detail, there is no body to display.

image

But what if you do want to provide more information, such as a more detailed message? The HttpResponseException provides properties that allow you to specify more information.

The Content property allows you to provide a response body.

The ReasonPhrase property is a textual description of the status code. You can add any text you would like, such as "Customer Id not found". This may be more useful to the client applications than getting a 404.

Change the exception in the prior example as shown below.

In  C#:

throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound) 
  { 
   Content = new StringContent("Could not locate a customer with id: "
                                                    
+ id),
   ReasonPhrase = "Customer Id not found"
  });

In VB:

Throw New HttpResponseException(New HttpResponseMessage(HttpStatusCode.NotFound) With
{
.Content = New StringContent("Could not locate a customer with id: " & 
                                                         id),
.ReasonPhrase = "Customer Id not found"
})

Repeating the F12 process one more time… Notice that the Response Body now includes the information provided in the Content property.

image

If you developed the JavaScript client as shown in this prior post, you’ll notice that it already has code to handle an exception. The result is shown below:

image

Use the HttpResponseException whenever you want to control the exception information passed to your ASP.NET Web service clients.

Enjoy!

September 24, 2012

Building a JavaScript Client for a Web API Service

Filed under: ASP.NET,ASP.NET Web API,C#,JavaScript,VB.NET @ 12:54 am
An ASP.NET Web API service provides support for many different types of client applications. One such client is a JavaScript application. This post provides a very simple JavaScript client you can use to test your Web API service.

[See this prior post for an introduction to building an ASP.NET Web API service. The service created in that post is the example service used here.]

Here is the HTML/JavaScript code to display all of the key/value pairs for a single object returned by a Web API service.

<!DOCTYPE html> <html lang=”en”> <head> <title>Customer Information</title> <script src=”../../Scripts/jquery-1.7.1.min.js” type=”text/javascript”></script> <script type=”text/javascript”> $(document).ready(function () { // Send an AJAX request for Customer Info $.getJSON(“api/customer/2″, function (data) { // On success, ‘data’ contains the customer details. $.each(data, function (key, val) { // Format the text to display. var str = key + ‘: ‘ + val;

                    // Add a paragraph for each property. $(‘#info’).append(‘<p>’ + str + ‘</p>’); }) })

             // Handle a fail .fail( function (jqXHR, textStatus, err) { $(‘#info’).html(‘Error: ‘ + err); }); }); </script> </head>

<body> <div> <h1>Customer Account Information</h1> <p id=’info’ /> </div> </body>

</html>

If you followed the prior post mentioned above and built the sample Web API service, you can delete all of the code from the Index.cshtml file and replace it with the above code.

When you run the application, you get this:

image

Let’s go through the JQuery script code. The example uses the 1.7.1 version of JQuery. The script itself does the following:
  • $.getJSON: Gets the JSON format of the data returned when passing “api/customer/2″ to the Web API service. You can change the url string passed to the service to match any url accepted by your service.
  • $.each: Iterates over each object, executing a function for each one. In this case, it processes each name/value pair provided by the JSON format.
    • It builds a string with the name of the field, a colon, and the value.
    • It adds the string to a paragraph element and then appends the paragraph to an element with an Id of #info.
  • .fail: Provides error information if an error is returned by the service.

The HTML code displays the header and a paragraph placeholder that is extended by the script.

If you want to display a set of business objects with their name/value pairs, you can change the script as follows:

<script type=”text/javascript”> $(document).ready(function () { // Send an AJAX request for Customer Info $.getJSON(“api/customer”, function (data) { // On success, ‘data’ contains the customers details. $.each(data, function (key, row) {

                $(‘#info’).append(‘<p>’ + ‘Item: ‘ + key + ‘</p>’); // Process each customer (“row”) $.each(row, function (rowKey, rowValue) {

                    // Format the text to display. var str = rowKey + ‘: ‘ + rowValue;

                    // Add a paragraph for each property. $(‘#info’).append(‘<p style=”text-indent: 5em;”>’ + str + ‘</p>’); }) }) })

         // Handle a fail .fail( function (jqXHR, textStatus, err) { $(‘#info’).html(‘Error: ‘ + err); }); }); </script>

This script includes two .each methods, the first one processes each customer and the second one processes each field of the customer from the JSON.

The result of this script is shown below:

image

Notice that both scripts could be used for any set of JSON objects returned from a Web API service. You just need to change the url string as appropriate for the service.

Enjoy!
September 23, 2012

Using IE 9 and F12 to Debug your Web API Service

Filed under: ASP.NET,ASP.NET Web API,C#,VB.NET @ 7:39 pm

Internet Explorer 9 allows you to see the HTTP request and response information using the F12 developer tools. This can be useful when debugging or testing your ASP.NET Web API service.

[See this prior post for an introduction to building an ASP.NET Web API service. The service created in that post is the example service used here.]

Start your Web API Service in Visual Studio, viewing it in Internet Explorer 9.

image

I always forget which key to press for the F12 developer tools, but if I recall correctly, it is F12. :-)

The tool window appears as shown below:

image

Select the Network tab and click the Start Capturing button to start capturing the HTTP request and response information. Then select the browser window and add "api/customer" (or "api/customerVB" for the VB service) to the address bar. Cancel the request to open or save the result.

Returning to the F12 window, you will see the HTTP request:

image

Select the api/Customer entry and click the Go to detailed view button. From there, you can view the Request header to see more details on the request. Click the Response body to see the response:

image

Right-click on the body and select Word Wrap from the context menu to view the JSON in a nicer layout:

image

Use the F12 tool whenever you need to debug or test your Web API service or any client calling your Web API service.

Enjoy!

The World’s Simplest Web API/POCO Example

Filed under: ASP.NET,ASP.NET Web API,C#,VB.NET @ 6:28 pm

This post demonstrates how to use the ASP.NET Web API to access plain old custom objects (POCO) and build a service for a line of business application. No Entity Framework (EF).

The Web API is a new easy-to-use framework that allows you to build a service that can send or receive data over HTTP to a range of clients. If this sounds something like Windows Communication Foundation (WCF), you are right. But this new framework is MUCH easier to use and provides content that is much easier to consume by client applications.

A Web API service can provide content to .NET clients, but also JavaScript clients and clients written in other languages such as Ruby or Python. It provides that content in different formats such as XML or JSON (JavaScript Object Notation).

The example in this post is as absolutely simple as possible to help you get up and running with these technologies. To meet this goal, this first post covers how to build a service that only provides data. Later posts will cover building clients to get the data, and updating the service for insert and update operations.

So first, the prerequisites:

Since Visual Studio 2012 is now officially released, the screen shots in this post are all of the Visual Studio 2012 tools.

STEP 1: Create the Web API project.

1) In Visual Studio, select File | New | Project

2) Select the ASP.NET MVC 4 Web Application under the Visual Basic or Visual C# templates and click OK.

image

3) Select Web API as the project template and click OK.

image

Visual Studio creates all of the files you need to get started with your Web API service.

image

The resulting code is complete enough to execute. If you run the application at this point (F5) it appears as follows:

image

To see the data provided by the default service, add "api/values" to the end of the string in the address bar. The result is something like: http://localhost:2152/api/values. You will then be asked to open or save the data returned by the service. Select to Open and if asked, select to open with Notepad. The result is:

image

So the default pre-built service returns two values in JSON format called "value1" and "value2". Yes, well it is a start.

The key parts of code required for a Web API service are the Models and the Controllers.

The Models provide the content for your service. They define which data or other content that your service will expose to client applications. For example, for the Acme Customer Management application, the models could include Customer, Purchase, and Invoice. These would expose basic customer information, customer purchasing information, and customer invoice information.

No models are created for you in this case. They are created manually as defined in the next step.

The Controllers basically control your service. They define the actions that your service accepts and performs the response appropriate for the requested action. For example the Acme Customer Management application would provide a Get action that would return the list of all of the customers and a Get action with a key that would provide information for a specific customer.

Two controllers are created in the generated code. The HomeController is a MVC controller which controls the generated sample user interface. The ValueController is the sample Web API controller that provided the two values shown in the screen shot above. You can delete it.

STEP 2: Add your Model

The service is this example provides customer information. That information is defined in the model with a Customer class.

1) Right-click on the Models folder in Solution Explorer and select Add | Class.

2) Add the code for a Customer class.

In C#:

using System.Collections.Generic;

namespace ACMService.Models
{
    public class Customer
    {
        public int CustomerId { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public string EmailAddress { get; set; }

        /// <summary>
        /// Retrieves a list of customers.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// In a "real" application, this code would
        /// call a data access component that retrieves
        /// the data from a database.
        /// </remarks>
        public static List<Customer> Retrieve()
        {
            List<Customer> custList = new List<Customer>
                    {new Customer()
                          { CustomerId = 1,
                            FirstName="Bilbo",
                            LastName = "Baggins",
                            EmailAddress = "bb@hob.me"},
                    new Customer()
                          { CustomerId = 2,
                            FirstName="Frodo",
                            LastName = "Baggins",
                            EmailAddress = "fb@hob.me"},
                    new Customer()
                          { CustomerId = 3,
                            FirstName="Samwise",
                            LastName = "Gamgee",
                            EmailAddress = "sg@hob.me"},
                    new Customer()
                          { CustomerId = 4,
                            FirstName="Rosie",
                            LastName = "Cotton",
                            EmailAddress = "rc@hob.me"}};
            return custList;
        }
    }
}

In VB:

”’ <summary>
”’ Manages a customer
”’ </summary>
”’ <remarks></remarks>
”’ <editHistory></editHistory>
Public Class CustomerVB
    Public Property CustomerId As Integer
    Public Property FirstName() As String
    Public Property LastName() As String
    Public Property EmailAddress() As String

    ”’ <summary>
    ”’ Retrieves a list of customers.
    ”’ </summary>
    ”’ <returns></returns>
    ”’ <remarks>
    ”’ In a "real" application, this code would
    ”’ call a data access component that retrieves
    ”’ the data from a database.
    ”’ </remarks>
    Public Shared Function Retrieve() As List(Of CustomerVB)
        Dim custList As New List(Of CustomerVB) From
                    {New CustomerVB() With
                          {.CustomerId = 1,
                            .FirstName = "Bilbo",
                            .LastName = "Baggins",
                            .EmailAddress = "bb@hob.me"},
                    New CustomerVB() With
                          {.CustomerId = 2,
                            .FirstName = "Frodo",
                            .LastName = "Baggins",
                            .EmailAddress = "fb@hob.me"},
                    New CustomerVB() With
                          {.CustomerId = 3,
                            .FirstName = "Samwise",
                            .LastName = "Gamgee",
                            .EmailAddress = "sg@hob.me"},
                    New CustomerVB() With
                          {.CustomerId = 4,
                            .FirstName = "Rosie",
                            .LastName = "Cotton",
                            .EmailAddress = "rc@hob.me"}}
        Return custList
    End Function
End Class

NOTE: This code does not use a data access layer at this time. To keep this example as simple as possible, the Retrieve method "mocks" a retrieve that would call your data access component and retrieve the data from the database. This code will be replaced in a later post to actually access a database.

NOTE: Normally I build a Customer class (singular) with the properties and methods that mange a singe customer and a Customers class (plural) that works with a list of those customers. The functionality for both were added to one class to keep this example as simple as possible.

Step 3: Add your Controller

The controller in this example provides two operations: one to get all customers, and one to get one customer by Id.

1) Right-click on the Controllers folder in Solution Explorer and select Add | Controller.

2) Select Empty API Controller

Visual Studio provides several scaffolding options, which define the type of code that it will automatically generate in the controller. To keep this example simple, it will only provide read actions, so select  Empty API Controller.

image

3) Write the code for the controller.

In C#:

using ACMService.Models;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

namespace ACMService.Controllers
{
    public class CustomerController : ApiController
    {
        // GET api/customer
        public IEnumerable<Customer> Get()
        {
            return Customer.Retrieve();
        }

        // GET api/customer/2
        public Customer Get(int id)
        {
            List<Customer> customerList = Customer.Retrieve();
            Customer customerInstance = customerList.FirstOrDefault(
                                c => c.CustomerId == id);
            return customerInstance;
        }
    }
}

In VB:

Imports System.Net
Imports System.Web.Http

Public Class CustomerVBController
    Inherits ApiController

    ‘ GET api/customerVB
    Public Function [Get]() As IEnumerable(Of CustomerVB)
        Return CustomerVB.Retrieve()
    End Function

    ‘ GET api/customerVB/2
    Public Function [Get](id As Integer) As CustomerVB
        Dim customerList = CustomerVB.Retrieve()
        Dim customerInstance = customerList.FirstOrDefault(
                    Function(c) c.CustomerId = id)
        Return customerInstance
    End Function
End Class

The controller class inherits from ApiController. This ensures that the service understands that this is a Web API controller class.

The class is called CustomerController (CustomerVBController in the VB example). The name should define the type of content the controller provides and end with "Controller". The service automatically locates the controller by name. To access the CustomerController, you would add "api/Customer" to the address bar and .NET automatically executes the Get method in the CustomerController class. (Enter api/CustomerVB to call the CustomerVBController.)

Both functions are named "Get". Note that in VB, "Get" is a keyword so it needs brackets around it to use it as a function name. But you could change the name to something more specific like GetCustomer.

The functions contain code to provide the requested data. The Get without any parameters retrieves the entire list. The Get with the id parameter uses LINQ with a Lambda expression to find the requested customer in the list. (For an overview of Lambda expressions, see this link.)

NOTE: This is not the most efficient approach to retrieve one customer. You would instead want to get the one requested customer from the database.

Now when you run the application, the same Welcome page appears because we have not changed the UI. And since we are building a service, we could actually remove the default UI.

To get the customer data, add "api/customer" to the address bar. ("api/customerVB" if you are running the VB example.)

image

If you open the results in Notepad, you will see the following:

image

If you instead add "api/customer/2" to the address bar ("api/customerVB/2" in VB), you get this:

image

Use this basic set of steps as a starting point for building your Web API service.

Enjoy!
February 9, 2010

ASP.NET: UpdatePanel and Master Pages

Filed under: ASP.NET,C#,VB.NET @ 7:21 pm

I had an ASP.NET page with two UpdatePanel controls. I wanted to handle the page refresh differently depending on whether the user clicked on the button in the first UpdatePanel or whether the click was on the button in the second UpdatePanel.

After a little time with Bing, I found the IsInAsyncPostBack and AsyncPostBackSourceElementId properties of the ScriptManager and thought I was good to go. But no. The page had no ScriptManager control because the ScriptManager was on the master page.

I had put the ScriptManager on the master page because almost every one of my pages needs some AJAX control or another. I didn’t want to move it onto every page.

So back to Bing for more research. I found several solutions for getting the ScriptManager from the master page, one of which required about 60 lines of code.

Then I found this one:

In C#:

protected void Page_Load(object sender, EventArgs e)
{
    if (this.IsPostBack)
    {
        ScriptManager sm = ScriptManager.GetCurrent(this.Page);
        if (sm != null && sm.IsInAsyncPostBack)
        {
            if (sm.AsyncPostBackSourceElementID ==
                           AddButton.UniqueID)
            {
                //  Do whatever
            }
            else
            {
                //  Do the other thing
            }
        }
    }
    else
    {
        // Setup the page text and populate lists. 
    }

}

In VB:

Protected Sub Page_Load(ByVal sender As Object, _
                    ByVal e As System.EventArgs) Handles Me.Load
    If IsPostBack Then
        Dim sm As ScriptManager = ScriptManager.GetCurrent(Me.Page)
        If sm IsNot Nothing AndAlso sm.IsInAsyncPostBack Then
            If sm.AsyncPostBackSourceElementID = _
                           AddButton.UniqueID Then
            ‘  Do whatever
            Else
            ‘  Do the other thing
            End If 
        End If
    Else
        ‘ Setup the page text and populate lists.
    End If

End Sub

This code uses the ScriptManager GetCurrent static method to find the ScriptManager associated with the page. It then uses that instance to check the IsInAsyncPostBack property. The AsyncPostBackSourceElementId provides the unique Id of the element on the page that generated the post back.

Use this technique whenever you need to obtain the ScriptManager from the master page.

Enjoy!

ASP.NET: Styling a GridView

Filed under: ASP.NET,C#,VB.NET @ 6:20 pm

This post provides some tips for styling the GridView control.

The prior post here demonstrates how to use the GridView with your business objects. This post builds upon that example and demonstrates how to style a GridView. Use these techniques any time you want to add some style to your GridView control, regardless of how the GridView was populated.

By default, a sorted and paged DataGrid looks something like this:

image

In this example, the application color scheme was a shade of orange, so the desired GridView design is as follows:

image

But you can replace the color with whatever color matches your design.

If you plan to use stylesheets, which I highly recommend, then styling your GridView requires two steps. First, define the desired stylesheet elements. Second, apply the style elements in the GridView tag.

Define the Stylesheet Elements

Define the desired stylesheet elements in the CSS file for your application. In this example, the stylesheet is called SampleStyleSheet.css.

In CSS:

/*———– Grid ———————————–*/
.GridHeader
{
    color:#C16914;           
    font-weight:bold;
}

.GridHeader a
{
    color:#C16914;           
    font-weight:bold;
}

.GridHeader a:active
{
    color:Black;           
    font-weight:bold;
}

.GridRow
{
    color:#C16914;
}

.GridAlternatingRow
{
    background-color:#f2f2f2;    /* Light gray */
    color:#C16914;                           
}

.GridPager
{
    color:#C16914;               
    font-weight:bold;
}

.GridPager a
{
    color:#C16914;               
    font-weight:normal;
}

In this example where the grid is sortable, the grid header is a hyperlink. So the .GridHeader CSS class is not really used. The .GridHeader a element is used to show the hyperlinks in the column header of the grid. The .GridHeader a:active element defines how the hyperlink should look when it is activated. By setting a different color in the a and a:active styles, the hyperlink changes color while the grid is sorted.

The .GridRow CSS class defines the style for the basic grid row. In this case, the text color is set to the orange color scheme, but you can set it to any color. If you want the standard rows to have a background color, you can set that as well.

The .GridAlternatingRow CSS class defines the style for every other row. In this example, the color remains the same as the normal row, but a light gray background color is set.

The .GridPager CSS class defines the style for the current page because the current page is *not* shown with a hyperlink. In this example, the current page is shown in bold.

The .GridPager a element defines the style for the other page numbers that are shown as hyperlinks.

Define the GridView

Define the GridView control in your ASP.NET page using the styles defined in the prior step.

In HTML:

<asp:GridView ID="CustomerGridView" runat="server"
    AllowPaging="true" PageSize="3"
    AllowSorting="true"
    AutoGenerateColumns="false">
    <HeaderStyle CssClass="GridHeader" />
    <RowStyle CssClass="GridRow" />
    <AlternatingRowStyle CssClass="GridAlternatingRow" />
    <PagerStyle CssClass="GridPager" />
    <Columns>
        <asp:BoundField HeaderText="Last Name"
            DataField="LastName" SortExpression="LastName" />
        <asp:BoundField HeaderText="First Name"
            DataField="FirstName" SortExpression="FirstName" />
        <asp:BoundField HeaderText="Email" 
            DataField="EmailAddress" SortExpression="EmailAddress" />
    </Columns>
</asp:GridView>

Notice that the styles are set in the HeaderStyle, RowStyle, AlternatingRowStyle, and PagerStyle elements within the asp:GridView tag.

Set the GridView styles any time you want your GridView to match your ASP.NET application design.

Enjoy!

February 1, 2010

ASP.NET: Mega Menus

Filed under: ASP.NET,C#,VB.NET @ 5:10 pm

I have been giving much thought to ASP.NET and Silverlight menus of late. While doing research on existing sites and how they are handling menus, I came across the concept of a "mega menu".

A mega menu is basically a drop down menu that contains many, many options. It provides a user with a quick way to navigate to a particular location on a site. Here is one example:

image

Hovering over the Technology tab displays a large set of menu options [highlight in blue is mine].

This looks like a very interesting and user-friendly way to provide the user with a large number of choices. And it is DEFINITELY better than lots of fly-out menus. Seems like a good design for eCommerce types of sites where you want your potential customer to quickly find your products.

Check out a set of good mega menu examples here.

This is not to say that every menu should be a mega menu. In fact, I find that most line of business applications (non-eCommerce) require a different approach. Primarily because line of business application often need to perform a set of related tasks and not just find products.

Enjoy!

Next Page »

© 2014 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs