Windows Azure Table Storage Part 1

This is the first part of a two part series on building a basic working web app using ASP.NET MVC to create, update, delete, and access views of the data in a Windows Azure Table Storage Service.  The second part will be published this next Monday.  With that stated, let’s kick off with some technical specifics about Windows Azure Table Storage.

The Windows Azure Table service provides a structured storage in the form of tables.  The table storage you would setup within Windows Azure is globally unique.  Any number of tables can be created within a given account with the requirement that each table has a unique name.

The table storage account is specified within a unique URI such as:

http://myaccount.table.core.windows.net

Within each table the data is broken into collections called entities.  Entities are basically rows of data, similar to a row in a spreadsheet or a row within a database.  Each entity has a required primary key and a set of properties.  The properties are a name, typed-value pair, similar to a column.

Tables, Entities, and Properties

There are three core concepts to know when dealing with Windows Azure Tables; Table, Entities, and Properties.  For each of these core features of the Windows Azure Table Storage it is important to be able to add, possibly update, and delete the respective table, entity, or property.

Windows Azure Table Hierarchy;

  • Table – Similar to a spreadsheet or table in a relational database.
  • Entity – Similar to a row of data in a spreadsheet, relational database, or flat file.
  • Property – Similar to a cell in a spreadsheet or tuple in a relational database.

Each entity has the following system properties; a partition key, row key, and time stamp.  These properties are included with every entity and have reserved naming.  The partition and row key are responsibilities of the developer to insert into, while the time stamp is managed by the server and is read only.

Three properties that are part of every table;

  • Partition Key
  • Row Key
  • Time Stamp

Each table name must conform to the following rules; a name may have only alphanumeric characters, may not begin with a numeric character, are case-insensitive, and must be between 3 and 63 characters.

Tables are split across many nodes for horizontal scaling.  The traffic to these nodes is load balanced.  The entities within a table are organized by partition.  A partition is a consecutive range of entities possessing the same key value, the key being a unique identifier within a table for the partition.  The partition key is the first part of the entity’s primary key and can be up to 1 KB in size.  This partition key must be included in every insert, update, and delete operation.

The second part of the primary key is the row key property.  It is a unique identifier that should not be read, set on insert or update, and generally left as is.

The Timestamp property is a DateTime data type that is maintained by the server to record the entity for last modifications.  This value is used to provide optimistic concurrency to table storage and should not be read, inserted, or updated.

Each property name is case sensitive and cannot exceed 255 characters.  The accepted practice around property names is that they are similar to C# identifiers, yet conform to XML specifications.  Examples would include; “streetName”, “car”,  or “simpleValue”.

To learn more about the XML specifications check out the W3C link here:  http://www.w3.org/TR/REC-xml/.  This provides additional information about properly formed XML that is relatable to the XML usage with Windows Azure Table Storage.

Coding for Windows Azure Tables

What I am going to show for this code sample is how to setup an ASP.NET MVC Application using the business need of keeping an e-mail list for merges and other related needs.

I wrote the following user stories around this idea.

  1. The site user can add an e-mail with first and last name of the customer.
  2. The site user can view a listing of all the e-mail listings.
  3. The site user can delete a listing from the overall listings.
  4. The site user can update a listing from the overall listings.

This will provide a basic fully functional create, update, and delete against the Windows Azure Table Storage.  Our first step is to get started with creating the necessary projects within Visual Studio 2010 to create the site with the Windows Azure Storage and Deployment.

  1. Right click on Visual Studio 2010 and select Run As Administrator to execute Visual Studio 2010.
  2. Click on File, then New, and finally Project.  The new project dialog will appear.
  3. Select the Web Templates and then ASP.NET MVC 2 Empty Web Application.
  4. Name the project EmailMergeManagement.  Click OK.
  5. Now right click on the Solution and select Add and then New Project.  The new project dialog will appear again.
  6. Select the Cloud Templates and then the Windows Azure Cloud Service.
  7. Name the project EmailMergeManagementAzure.  Click OK.
  8. When the New Cloud Service Project dialog comes up, just click OK without selecting anything.
  9. Right click on the Roles Folder within the EmailMergeManagementAzure Project and select Add and then Web Role Project in Solution.
  10. Select the project in the Associate with Role Project Dialog and click OK.

The Solutions Explorer should have the follow projects, folders, files, and Roles setup.

Solution Explorer
Solution Explorer
  1. Now create controller classes called StorageController and one called HomeController.
  2. Now a Storage and Home directory in the Views directory.
  3. Add a view to each of those directories called Index.aspx.
  4. In the Index.aspx view in the Home directory add the following HTML.

[sourcecode language=”html”]
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

<%:Html.Encode(ViewData["Message"])%>
<h1>
<%:Html.Encode(ViewData["Message"])%></h1>
This ASP.NET MVC Windows Azure Project provides examples around
the Windows Azure Storage usage utilizing the Windows Azure SDK.
<ul>
<li>
<%:Html.ActionLink("Windows Azure Table Storage", "Index", "Storage")%></li>
</ul>
[/sourcecode]

  1. In the Storage directory Index.aspx view add the following code.

[sourcecode language=”html”]
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

<%: Html.Encode(ViewData["Message"])%>
<h1>
<%: Html.Encode(ViewData["Message"])%></h1>
[/sourcecode]

  1. In the StorageController add this code.

[sourcecode language=”csharp”]
using System.Web.Mvc;

namespace EmailMergeManagement.Controllers
{
public class StorageController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Windows Azure Table Storage Sample";
return View();
}
}
}
[/sourcecode]

  1. In the HomeController add this code.

[sourcecode language=”csharp”]
using System.Web.Mvc;

namespace EmailMergeManagement.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Windows Azure Storage Samples";
return View();
}
}
}
[/sourcecode]

Now the next step is to get our Models put together.  This section will include putting together the class for the Email Merge Listing Model, the repository class for getting the data in and out of the table, and the context object that is used for connecting to the actual Development Fabric or Windows Azure Table Storage.

Solution Explorer
Solution Explorer
  1. First add the following references; System.Data.Services.Client,  Microsoft.WindowsAzure.CloudDrive, Microsoft.WindowsAzure.Diagnostics, Microsoft.WindowsAzure.ServiceRuntime, and Microsoft.WindowsAzure.StorageClient to the project by right clicking on the References virtual folder for the EmailMergeManagement Project.
  2. Once you add these references create a class in the Models folder called EmailMergeModel and add the following code.  I’ve added some basic validation attributes to the Email, First, and Last Properties of the EmailMergeModel Class just so that it has a little more semblance of something you may actually see in real world use.

[sourcecode language=”csharp”]
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.WindowsAzure.StorageClient;

namespace EmailMergeManagement.Models
{
public class EmailMergeModel : TableServiceEntity
{
public EmailMergeModel(string partitionKey, string rowKey)
: base(partitionKey, rowKey)
{
}

public EmailMergeModel()
: this(Guid.NewGuid().ToString(), string.Empty)
{
}

[Required(ErrorMessage = "Email is required.")]
[RegularExpression("^[a-z0-9_\\+-]+(\\.[a-z0-9_\\+-]+)*@[a-z0-9_\\+-]+(\\.[a-z0-9_\\-]+)*\\.([a-z]{2,4})$",
ErrorMessage = "Not a valid e-mail address.")]
public string Email { get; set; }

[Required(ErrorMessage = "First name is required.")]
[StringLength(50, ErrorMessage = "Must be less than 50 characters.")]
public string First { get; set; }

[Required(ErrorMessage = "Last name is required.")]
[StringLength(50, ErrorMessage = "Must be less than 50 characters.")]
public string Last { get; set; }

public DateTime LastEditStamp { get; set; }
}
}
[/sourcecode]

  1. Now add a class titled EmailMergeDataServiceContext for our data context.  This class provides the basic TableServiceContext inheritance that allows for creation of the table, entities, and properties through the Windows Azure SDK.
  2. Add the following code to the EmailMergeDataServiceContext Class.

[sourcecode language=”csharp”]
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace TestingCloudsWebApp.Models
{
public class EmailMergeDataServiceContext : TableServiceContext
{
public const string EmailMergeTableName = "EmailMergeTable";

public EmailMergeDataServiceContext(string baseAddress, StorageCredentials credentials)
: base(baseAddress, credentials)
{
}

public IQueryable EmailMergeTable
{
get { return CreateQuery(EmailMergeTableName); }
}
}
}
[/sourcecode]

  1. Create a class in the Models directory called EmailMergeRepository.  This is the class I will use to add the insert, update, and delete functionality.
  2. Now add a constructor and private readonly EmailMergeDataServiceContext member as shown below.

[sourcecode language=”csharp”]
private readonly EmailMergeDataServiceContext _serviceContext;

public EmailMergeRepository()
{
var storageAccount = CloudStorageAccount.
FromConfigurationSetting("DiagnosticsConnectionString");
_serviceContext =
new EmailMergeDataServiceContext(
storageAccount.TableEndpoint.ToString(),
storageAccount.Credentials);

storageAccount.CreateCloudTableClient().
CreateTableIfNotExist(
EmailMergeDataServiceContext.EmailMergeTableName);
}
[/sourcecode]

  1. Next add the select and get by methods to retrieve EmailMergeModel Objects.

[sourcecode language=”csharp”]
public IEnumerable<EmailMergeModel> Select()
{
var results = from c in _serviceContext.EmailMergeTable
select c;

var query = results.AsTableServiceQuery();
var queryResults = query.Execute();

return queryResults;
}

public EmailMergeModel GetEmailMergeModel(string rowKey)
{
EmailMergeModel result = (from c in _serviceContext.EmailMergeTable
where c.RowKey == rowKey
select c).FirstOrDefault();
return result;
}
[/sourcecode]

  1. Next add a method to add our custom date & time stamp for inserts and updates.

[sourcecode language=”csharp”]
private static EmailMergeModel StampIt(EmailMergeModel emailMergeModel)
{
// This is a sample of adding a cross cutting concern or similar functionality.
emailMergeModel.LastEditStamp = DateTime.Now;
return emailMergeModel;
}
[/sourcecode]

  1. Finally the delete, insert, and update methods can be added.

[sourcecode language=”csharp”]
public void Delete(EmailMergeModel emailMergeModelToDelete)
{
_serviceContext.Detach(emailMergeModelToDelete);
_serviceContext.AttachTo(EmailMergeDataServiceContext.EmailMergeTableName,
emailMergeModelToDelete, "*");
_serviceContext.DeleteObject(emailMergeModelToDelete);
_serviceContext.SaveChanges();
}

public void Insert(EmailMergeModel emailMergeModel)
{
_serviceContext.AddObject(EmailMergeDataServiceContext.EmailMergeTableName, StampIt(emailMergeModel));
_serviceContext.SaveChanges();
}

public void Update(EmailMergeModel emailMergeModelUpdate)
{
var emailMergeModelOld = GetEmailMergeModel(emailMergeModelUpdate.RowKey);
Delete(emailMergeModelOld);
Insert(StampIt(emailMergeModelUpdate));
}
[/sourcecode]

  1. At this point the Solution Explorer should have the following files and structure.
Storage Explorer
Storage Explorer

That’s it for part 1 of this two part series.  The next entry I’ll have posted this coming Monday, so stay tuned for the final steps.  🙂

Shout it

Monster Bits of WCF and Itty Bitty Bits of MVC

I’ve had the pleasure of working with WCF on three specific projects that have brought me to this blog entry.  I haven’t used WCF on only three projects, there are just three that have brought me to write this entry.  I’ve used WCF a lot, since back when it was a beta.  WCF is great when creating SOAP services and you aren’t too worried about the extra overhead.  WCF is great for what it does, for the ideas behind what it does.

But writing RESTful web services doesn’t seem to be its strong point.  On two huge projects WCF has basically been dropped, or so scaled back one really can’t honestly say that WCF is used, and either an alternate framework has been used or a LOT of custom code ends up being written.

The first time I used WCF to implement RESTful service was at Webtrends.  Albeit, there is a single service that returns all types of awesome reporting goodness, however to implement basic auth, logging, polling, and a whole host of other Enterprise Scale needs we had to custom roll most of it.  Keep in mind, when doing this the WCF REST capabilities were brand shiny and new, so there were a few issues to work out.  Now, maybe WCF could be used and a lot of it would be built in.  However as it was, we easily spent 60% of the time writing custom bits because WCF just didn’t have the right options with the right bindings.

But I digress, I recently implemented an architecture using RESTful services using WCF.  But now I’ve come to find myself dropping WCF because of the back and forth and going with ASP.NET MVC controller actions to return JSON instead.  With that, here’s to the lean mean controller actions rockin’ the JSON.  Here’s what I’ve done to port everything from WCF to MVC.

To see what I had done, except on a smaller scale, check out my previous blog entry on ASP.NET MVC with a WCF project smack in the middle of it.  This will give you an idea of what I was using the WCF services for, merely to provide JSON results via RESTful services to an ASP.NET MVC front end requesting data with jQuery.

This is how I’ve setup the controller to return JSON results via an action.

First start a new ASP.NET MVC Project and add a new controller.  Cleanup the controller so that you have the following in the controller.

[sourcecode language=”csharp”]
using System.Web.Mvc;

namespace RestWebServicesWithMvc.Controllers
{
public class ServicesController : Controller
{

}
}
[/sourcecode]

Now create a testing project to create your test first.  Remember to add the reference to the ASP.NET MVC project.  From here we can create the first test.

[sourcecode language=”csharp”]
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RestWebServicesWithMvc.Controllers;

namespace RestWebServicesWithMvc.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var controller = new ServicesController();
var result = controller.GetBiz();
Assert.IsNotNull(result);
}
}
}
[/sourcecode]

Now fill out the basic skeleton of the action in the controller.

[sourcecode language=”csharp”]
using System;
using System.Web.Mvc;

namespace RestWebServicesWithMvc.Controllers
{
public class ServicesController : Controller
{
public ActionResult GetBiz()
{
throw new NotImplementedException();
}
}
}
[/sourcecode]

Now we should have a good red running on our test.  Let’s create a business model class to return as our result next.

[sourcecode language=”csharp”]
namespace RestWebServicesWithMvc.Models
{
public class BizEntity
{
public string BizName { get; set; }
public string StartupDate { get; set; }
public int SalesThisMonth { get; set; }
}
}
[/sourcecode]

Now let’s return that object with some fake data.  First add [AcceptVerbs(HttpVerbs.Post)] to the action in the controller.  Then return a serializable object to the actual method as shown.

[sourcecode language=”csharp”]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult GetBiz()
{
return Json(
new BizEntity()
{
BizName = "Adron’s Code Workingz",
SalesThisMonth = 3429,
StartupDate = DateTime.Now.AddYears(-5).ToString()
}
);
}
[/sourcecode]

This is a quick starter.  There are a few dozen other options around this capability including other verb usage.  For many, this is all you need for your services, especially if their primary purpose is to communicate with a specific website and one doesn’t want the overhead of WCF.

Shout it

Exam 70-515 Focus Developing a Web Application by Using ASP.NET MVC 2

…and with this entry, I’ve skipped to the last section of the exam topics related to ASP.NET MVC 2.  Reading and studying the old viewstate based ASP.NET material was a bit rough, which I’ll be writing about in an upcoming entry.  For now, back to focusing on the test at hand.  The MVC Framework is pretty rock star awesome, so with that I’ll get to the notes.

Focus Point:  Developing a Web Application by Using ASP.NET MVC 2

Create Custom Route

  • ASP.NET Routing – ASP.NET enables you to use URLs that do not have a map to specific files in a Web Site.  Because the URL does not have a map to a file, you can use URLs that are descriptive of the user’s action and therefore are more easily understood by users.  Check out URL Patterns for more information (on the same page) and Adding Routes to an MVC Application.  Also for real world implementation you may want to know the difference between Routing and URL Rewriting.
  • Adding Constraints to Routes – In addition to matching the URL request to a route definition by the number of parameters in the URL, you can specify that values in the parameters meet certain constraints.  If a URL contains values that are outside the constraints for a route, that route is not used to handle the request.  You add constraints to make sure that the URL parameters contain values that will work in your application.  Constraints are defined by using regular expressions or by using objects that implement the IRouteConstraint interface. When you add the route definition to the Routes collection, you add constraints by creating a RouteValueDictionary object that contains the verification test. The key in the dictionary identifies the parameter that the constraint applies to. The value in the dictionary can be either a string that represents a regular expression or an object that implements the IRouteConstraint interface. If you provide a string, routing treats the string as a regular expression and checks whether the parameter value is valid by calling the IsMatch method of the Regex class. The regular expression is always treated as case-insensitive. For more information, see .NET Framework Regular Expressions.  If you provide an IRouteConstraint object, ASP.NET routing checks whether the parameter value is valid by calling the Match method of the IRouteConstraint object. The Match method returns a Boolean value that indicates whether the parameter value is valid.
  • Route Class – Provides properties and methods for defining a route and for obtaining information about the route.  The Route class enables you to specify how routing is processed in an ASP.NET application. You create a Route object for each URL pattern that you want to map to a class that can handle requests that correspond to that pattern. You then add the route to the Routes collection. When the application receives a request, ASP.NET routing iterates through the routes in the Routes collection to find the first route that matches the pattern of the URL.  Set the Url property to a URL pattern. The URL pattern consists of segments that come after the application name in an HTTP request. For example, in the URL http:www.contoso.com/products/show/beverages, the pattern applies to products/show/beverages. A pattern with three segments, such as{controller}/{action}/{id}, matches the URL http:www.contoso.com/products/show/beverages. Each segment is delimited by the / character. When a segment is enclosed in braces ( { and } ), the segment is referred to a URL parameter. ASP.NET routing retrieves the value from the request and assigns it to the URL parameter. In the previous example, the URL parameter action is assigned the value show. If the segment is not enclosed in braces, the value is treated as a literal value.  Set the Defaults property to a RouteValueDictionary object that includes values that are used if a parameter is missing from the URL or to set additional values that are not parameterized in the URL. Set the Constraints property to a RouteValueDictionary object that contains values that are either regular expressions orIRouteConstraint objects. These values are used to determine whether a parameter value is valid.
  • Route.Constraints Property – Gets or sets a dictionary of expressions that specify valid values for a URL parameter.  The Constraints property enables you to limit acceptable values for a URL parameter that is processed by a route. You assign a RouteValueDictionary object to the Constraints property. Each element in the RouteValueDictionary object contains the name of a parameter and one of the following:
    • A string that defines a regular expression. The regular expression is case-insensitive.
    • An object that implements the IRouteConstraint interface and that includes a Match method.

The following example is from the MSDN, which shows allowed methods of GET and POST.

[sourcecode language=”csharp”]
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}

public static void RegisterRoutes(RouteCollection routes)
{
string[] allowedMethods = { "GET", "POST" };
HttpMethodConstraint methodConstraints = new HttpMethodConstraint(allowedMethods);

Route reportRoute = new Route("{locale}/{year}", new ReportRouteHandler());
reportRoute.Constraints = new RouteValueDictionary { { "httpMethod", methodConstraints } };

routes.Add(reportRoute);
}
[/sourcecode]

Create Controllers and Actions

  • Controller and Action Methods in MVC Applications – The ASP.NET MVC Framework maps URLs to classes that are referred to as controllers.  Controllers process incoming requests, handle user input and interactions, and execute appropriate application logic.  A controller class typically calls a separate view component to generate the HTML markup for the request.  The base class for all controllers is the ControllerBase class, which provides general MVC handling.
  • Two great articles;  MSDN Magazine titled Building Web Apps without Web Forms and there is the What’s New section on the ASP.NET MVC Site.
  • Create an action – You add a new action to a controller by adding a new method to the controller. These methods must meet these requirements:
    • Must be public
    • Cannot contain a static method
    • Cannot be an extension method
    • Cannot be a constructor, getter, or setter
    • Cannot have open generic types
    • Cannot contain out or ref parameters
    • is not a method of the controller base class.
  • Create a Controller – This tutorial covers creating a controller including the menu options and scaffolding, etc.
  • Action Filtering in MVC Applications – In ASP.NET MVC, controllers define action methods that usually have a one-to-one relationship with possible user interactions, such as clicking a link or submitting a form.  For example, when a user clicks a link, a request is routed to the designated controller, and the corresponding action method is called.  ASP.NET MVC provides action filters that are custom attributes that provide a declarative means to add pre-action and post-action behavior to controller action methods.

MVC 2 Template Project

Structure an ASP.NET MVC Application

  • MVC Framework and Application Structure – In an ASP.NET website, URLs typically map to files that are stored on disk (usually .aspx files).  These .aspx files include markup and code that is processed in order to respond to the request. The ASP.NET MVC framework maps URLs to server code differently than an ASP.NET Web Forms page. Instead of mapping URLs to ASP.NET pages or handlers, the framework maps URLs to controller classes. Controller classes handle incoming requests, such as user input and interactions, and execute appropriate application and data logic, based on user input. A controller class typically calls a separate view component that generates HTML output as the response. The ASP.NET MVC framework separates the model, view, and controller components.
  • Global URL Routing Defaults see below (from MSDN Article)

[sourcecode language=”csharp”]
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);

}

protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
[/sourcecode]

Create and Customize Views

Using the begin BeginForm Helper

[sourcecode language=”html”]
<% using(Html.BeginForm("HandleForm", "Home")) %>
<% { %>
<!– Form content goes here –>
<% } %>
[/sourcecode]

Using it declaratively…

[sourcecode language=”html”]
<% Html.BeginForm(); %>
<!– Form content goes here –>
<% Html.EndForm(); %>
[/sourcecode]

Using the Checkbox Helper

[sourcecode language=”html”]
<%= Html.CheckBox("bookType") %>
[/sourcecode]

Using the DropDownList Helper

[sourcecode language=”html”]
<%= Html.DropDownList("pets") %>
[/sourcecode]

[sourcecode language=”csharp”]
List<string> petList = new List<string>();
petList.Add("Dog");
petList.Add("Cat");
petList.Add("Hamster");
petList.Add("Parrot");
petList.Add("Gold fish");
petList.Add("Mountain lion");
petList.Add("Elephant");

ViewData["Pets"] = new SelectList(petList);
[/sourcecode]

Using the RadioButton Helper

[sourcecode language=”html”]
Select your favorite color:<br />
<%= Html.RadioButton("favColor", "Blue", true) %> Blue <br />
<%= Html.RadioButton("favColor", "Purple", false)%> Purple <br />
<%= Html.RadioButton("favColor", "Red", false)%> Red <br />
<%= Html.RadioButton("favColor", "Orange", false)%> Orange <br />
<%= Html.RadioButton("favColor", "Yellow", false)%> Yellow <br />
<%= Html.RadioButton("favColor", "Brown", false)%> Brown <br />
<%= Html.RadioButton("favColor", "Green", false)%> Green
[/sourcecode]

Using the Textbox Helper

[sourcecode language=”html”]
Enter your name: <%= Html.TextBox("name") %>
[/sourcecode]

Exam 70-515 Pt 1 Focus on Developing Web Forms Pages

I’m going to be taking the Web Application Development with Microsoft .NET Framework 4 in the very near future.  Since I’m going to be scribbling a few notes and studying up, it seemed like I’d publish my studies.  With that, here is round 1.  My intent is to study each focus area and make a blog post about it.  Beware, these could be some rather messy blog entries, I’ll try to keep them somewhat cohesive though. (Note, the base of these notes are from the actual 70-515 curriculum and also Niall Merrigan’s Blog Entry “MCTS Web Applications .NET 4 – 70-515 – Objectives List Part 1” helped a lot!  I’ve of course added my extra 2 cents to most of these.  Cheers!)

Focus Point:  Developing Web Forms Pages

Configure Web Forms Pages

A page directive is a page-specific attribute used by the ASP.NET page parser and compiler.  Some of these attributes I’ve linked and detailed below:

  • Page.AsyncMode Property – Sets a value indicating the page is processed synchronously or asynchronously.  The AsyncMode property is set by the Page parser when code for the page is generated. Use the Async attribute in @ Page directive to set this value.  Asynchronous pages do not work when the AspCompat attribute is set to true or the Transaction attribute is set to a value other than Disabled in the @ Page directive.
  • Page.AsyncTimeout Property – Gets or sets a value indicating the time-out interval used when processing asynchronous tasks.  The asynchronous time-out of the page represents the amount of time that the page will wait to perform asynchronous tasks. In most circumstances, do not set this property in code. Set the page asynchronous time-out interval using the pages element of the Web configuration file or in the @ Page directive. Values set in the page configuration section are overwritten by the page directive.  Define your asynchronous task using the PageAsyncTask class and register a beginning, an ending, and a time-out handler. If the asynchronous task does not complete in the time interval specified, the time-out handler will be invoked.
  • Page.AspCompatMode Property – Sets a value indicating whether the page can be executed on a single-threaded apartment (STA) thread.  This allows the page to call STA components, such as components developed with Visual Basic 6.0. Setting this property to true also allows the page to call COM+ components that require access to the unmanaged ASP built-in objects. These are accessible through the ASP ObjectContext object or the OnStartPage method.  In most circumstances, do not set this property in code. Set the aspcompat attribute to true using the @ Page directive in the .aspx file. When the page is requested, the dynamically generated class sets the property.
  • Page.Buffer Property – Sets a value indicating whether the page output is buffered.  In most circumstances, do not set this property in code. Set the Buffer attribute to true using the @ Page directive in the .aspx file. When the page is requested, the dynamically generated class sets the property.
  • Control.ClientIDMode Property – Gets or sets the algorithm that is used to generate the value of the ClientID property.  The values are AutoID which generates the value by concatenating the ID values of each parent naming container with the ID value of the control, seperated by an underscore character.  The Static value sets the value statically and makes the control the top of the hierarchy of naming containers if it is a container.  Predictable algorithm is used for controls that are data-bound controls and concatenates the ClientID value of the parent with the ID value of the control.  Inherit sets it to the parent control.
  • Page.ClientTarget Property – Gets or sets a value that allows you to override automatic detection of browser capabilities and to specify how a page is rendered for particular browser clients.  If you do not set the ClientTarget property, the HttpBrowserCapabilities object associated with the Page.Request property reflects the browser capabilities associated with the value (alias) that you provide.  uplevel, which specifies
  • CompilationMode Enumeration – Defines constants that specify how ASP.NET should compile .aspx pages and .ascx controls.  Members include; Auto sets ASP.NET to not compile the page if possible, Never states the page will not be dynamically compiled, and Always sets ASP.NET to compile every time.
  • Control.EnableViewState Property – This one has been the disdain of many serious web developers, as the view state becomes a horrifying beast to deal with.  But I digress, it may appear on the test so know about it.  🙂  This property gets or sets a value indicating whether the server control persists its view state, and the view state of any child controls it contains, to the requesting client.
  • Page.EnableViewStateMac Property – Gets or sets a value indicating whether ASP.NET should check message authentication codes (MAC) in the page’s view state when the page is posted back from the client.
  • Page.ErrorPage Property – Gets or sets the error page to which the requesting browser is redirected in the event of an unhandled page exception.
  • Page.MasterPageFile Property – Gets or sets the file name of the master page.
  • Page.MetaDescription – Gets or sets the content of the “description” meta element.
  • Page.MetaKeywords – Gets or set the content of the “keywords” meta element.
  • Page.Theme Property – Gets or sets the name of the page theme.
  • ViewStateEncryptionMode Enumberation – Specifies whether view-state information is encrypted;  Auto sets the encryption by calling the RegisterRequiresViewStateEncryption method, Always sets it to encrypt always and Never does not encrypt even if the control requests it.
  • Control.ViewState Property – Gets or sets the view-state mode of this control.
  • Control.ViewStateMode Property – Gets or sets the view-state mode of this control.
  • Compiler.WarningLevel Property – Gets the compiler warning level.
  • Saving and reading values in View State:  Save Values / Read Values
  • Auto Event Wire Up – i.e. ASP.NET Web Server Control Event Model.
  • PageSection.EnableEventValidation Property – Gets or sets a value that specifies whether event validation is enabled.
  • General configuration settings (ASP.NET) – This topic covers web.config files and setting up configuration sections within this file.  Some of the topics to be familiar with include; connectionStringssystem.web, configSections, appSettings, location, System.Configuation, and System.Web.Configuration.
  • XHTML Standards in Visual Studio and ASP.NET
  • Editing ASP.NET Configuration Files – Case-sensitivity, custom sections, remote configuration files, etc.
  • Making controls easier to access via JavaScript since they’re often cryptic…  Walkthrough:  Making Controls Located in Web User Controls Easier to Access from JavaScript, How to:  Access Controls from JavaScript by ID, and Walklthrough:  Making Data-Bound Controls Easier to Access from JavaScript.

Implement Master Pages and Themes

Implement Globalization

Handle Page Life Cycle Events

Implement Caching

There are a few more topics for this section of the test, I’ll follow up with the remainder of those notes, and move on to the next topic section.  Enjoy…

Aggregated Web Services Pt II – Tying it Together

I’ve been working through a project recently that I ended up creating an interesting abstraction of an assembly/classes between multiple web services projects.  I wrote about it initially in Aggregated Web Services Pt I.  In this blog entry is going to cover a few things, based on a full end-to-end implementation of a project from the WCF RESTful Web Services, to the ASP.NET MVC Site, and finally the jQuery calling those same services.

Simple Architecture
Simple Architecture

Before I got started, there is one thing I need to point out.  The communication with Javascript/jQuery/AJAX has a lot of tricky bits one needs to be aware of.  One of those is the same site origin and of course the famous cross domain solution issues.  That is why in this walk through I will place the web services and the site pages in the same project, yes, WCF and MVC living happily in a single project.  🙂  You can of course, if your architecture requires it, break these into separate projects, but for this example I’ll have both in the same project.

First create a new solution.  I always like to start with a new solution because it keeps the naming structured right, just from the practice.

(Click on any of the images to see a larger full size copy)

New Solution
New Solution

Once you create all of that then add an ASP.NET MVC 2 Project.

Adding the ASP.NET MVC 2 Project
Adding the ASP.NET MVC 2 Project

You can create an ASP.NET MVC 2 Unit Test Project if you want to, but I’m skipping it for now.  (yes, I’m still a big TDD advocate, but just setting up a prototype for this example)

Next I wiped out some files I don’t use, and suggest you zap em’ too.

Get Rid of the Nasty MS AJAX
Get Rid of the Nasty MS AJAX

Setting up the WCF Parts

Now that we’ve cleaned up those nasty bits, we’ll add our basic model we’ll be using.

[sourcecode language=”csharp”]
using System;

namespace EndToEnd.Mvc.Models
{
public class Investor
{
public string Id { get; set; }
public string Text { get; set; }
public decimal Money { get; set; }
public DateTime Stamp { get; set; }
}
}
[/sourcecode]

Now add a interface for the RESTful services to the root of the MVC Project as shown below.

[sourcecode language=”csharp”]
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;
using EndToEnd.Mvc.Models;

namespace EndToEnd.Mvc
{
[ServiceContract]
public interface IEndToEndService
{
[OperationContract]
[WebGet(UriTemplate = "Investors/{pageStart}/{pageEnd}", ResponseFormat = WebMessageFormat.Json)]
List<Investor> GetIncidents( string pageStart, string pageEnd);

[OperationContract]
[WebGet(UriTemplate = "Investor/", ResponseFormat = WebMessageFormat.Json)]
Investor GetInvestor();
}
}
[/sourcecode]

Now add the following abstract base class at the root level also.

[sourcecode language=”csharp”]
using System;
using System.Collections.Generic;
using EndToEnd.Mvc.Models;

namespace EndToEnd.Mvc
{
public abstract class InvestorBase : IEndToEndService
{
#region IEndToEndService Members

public List<Investor> GetIncidents(string pageStart, string pageEnd)
{
return new List<Investor>
{
new Investor
{
Id = Guid.NewGuid().ToString(),
Money = (decimal) (DateTime.Now.Second*2.27),
Stamp = DateTime.Now,
Text = "Lorum ipsum 1"
},
new Investor
{
Id = Guid.NewGuid().ToString(),
Money = (decimal) (DateTime.Now.Second*1.32),
Stamp = DateTime.Now,
Text = "Lorum ipsum 2"
}
};
}

public Investor GetInvestor()
{
return new Investor
{
Id = Guid.NewGuid().ToString(),
Money = (decimal) (DateTime.Now.Second*1.27),
Stamp = DateTime.Now,
Text = "Lorum ipsum"
};
}

#endregion
}
}
[/sourcecode]

Now add a WCF Service file and remove the interface file.  Then change the WCF class itself as shown below.  The reasons for the abstract class, inheriting from the interface, is that it removes any manipulation being needed with the actual *.svc file.  It just seems, at least to me, a little bit cleaner this way.

[sourcecode language=”csharp”]
namespace EndToEnd.Mvc
{
public class EndToEndService : InvestorBase
{}
}
[/sourcecode]

For the last touches for the WCF RESTful Service we need to setup the Web.Config file.  I’ve added the section toward the bottom of the config file in the <System.ServiceModel> section.  I’ve included the full config file below, so you can easily just copy and paste it if you’re working through step by step with me.

[sourcecode language=”html”]
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>
<pages>
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="httpBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServicesBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<services>
<service behaviorConfiguration="ServicesBehavior"
name="EndToEnd.Mvc.EndToEndService">
<endpoint address="" behaviorConfiguration="httpBehavior" binding="webHttpBinding"
contract="EndToEnd.Mvc.IEndToEndService" />
</service>
</services>
</system.serviceModel>

</configuration>
[/sourcecode]

One of the things I always do at this point is to setup the project properties.  I do this for a number of reasons, primarily to assure that the port number doesn’t go and change itself on me.  The other thing I set is the default startup page.  With ASP.NET MVC things get out of sync with Visual Studio, and Visual Studio tries to startup actual *.aspx files.  So what I do is just set the startup to an empty root startup.  These settings are shown below.

Project Properties
Project Properties

Setting up the MVC Parts

First add a home directory, a HomeController.cs, and then add a Core.Master master page to the project.

MVC Project Parts
MVC Project Parts

Next setup the Core.Master file with the following content sections.

[sourcecode language=”html”]
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;

<html xmlns="http://www.w3.org/1999/xhtml&quot; >
<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>

<script type="text/javascript" src="../../Scripts/jquery-1.4.1.js"></script>

<asp:ContentPlaceHolder ID=HeaderContent runat=server>
</asp:ContentPlaceHolder>

</head>
<body>
<div>
<asp:ContentPlaceHolder ID="MainContent" runat="server">
</asp:ContentPlaceHolder>
</div>
</body>
</html>
[/sourcecode]

One of the things I’ll point out is that with Visual Studio 2010 you get Intellisense with jQuery.  The reason I don’t use the x.x.min.js version of the jQuery is that it doesn’t have the appropriate setup to provide the Intellisense.  So be sure for development to use the fully expanded version and you can go to the zipped min version when you go live in production.  Another thing I do, which may vary on how you want to develop, is use the hosted jQuery on Google or Microsoft.  I did a write up previously for using the hosted jQuery so check it out for reference locations.

In the controller add the following code.

[sourcecode language=”csharp”]
using System.Web.Mvc;

namespace EndToEnd.Mvc.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
}
[/sourcecode]

Now that we have the Site.Master and the home controller, create an Index.aspx View in the Home folder of the project.

Adding the Index.aspx View
Adding the Index.aspx View

In the view add the following code for the jQuery calls to the services layer.

[sourcecode language=”html”]
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Core.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
jQuery AJAX Calls to RESTful WCF Web Services
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="HeaderContent" runat="server">
<script type="text/javascript">
var domainRoot = "http://localhost:1000/EndToEndService.svc/&quot;;
var investorUri = domainRoot + "investor/";
var investorsUri = domainRoot + "investors/10/15";
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
jQuery AJAX Calls to RESTful WCF Web Services
</h2>

<div id="InvestorUri">
</div>
<div id="InvestorResult">
</div>

<hr />

<div id="InvestorsUri">
</div>
<div id="InvestorsResult">
</div>

<script type="text/javascript">

$(‘#InvestorUri’).html(investorUri);
$(‘#InvestorsUri’).html(investorsUri);

$.getJSON(investorUri, function investor_complete(json) {
$(‘#InvestorResult’).html(‘<li>’ + json.Id + ‘</li>’ + ‘<li>’ + json.Text + ‘</li>’);
});

$.getJSON(investorsUri, function investors_complete(json) {
var builtHtml = ”;

$.each(json, function (x, y) {
builtHtml += ‘<li>’ + json[x].Id + ‘</li>’ + ‘<li>’ + json[x].Text + ‘</li>’;
});

$(‘#InvestorsResult’).html(builtHtml);
});

</script>
</asp:Content>

[/sourcecode]

Run it and you should get the following displayed on screen.

Browser Results
Browser Results

Let me know if you run into any issues trying this out.  Thanks!

Shout it