Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Windows Azure, PHP, ...

About the author

Maarten Balliauw is currently employed as a Technical Evangelist at JetBrains. His interests are mainly web applications developed in ASP.NET (C#) or PHP and the Windows Azure cloud platform.
More about me More about me
Send mail E-mail me


ASP.NET MVC Quickly Pro NuGet Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn
Maarten Balliauw - MVP - Most Valuable Professional
Maarten Balliauw - ASPInsider

Search

Archive

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright Maarten Balliauw 2013


Form validation with ASP.NET MVC release candidate

Last week, the ASP.NET MVC framework release candidate was released (check ScottGu’s post). Apart from some great new tooling support, form validation has never been easier. Here’s a quick introduction.

Employee from Northwind database Imagine we have a LINQ to SQL data model, containing an Employee from the Northwind database. As you may know, LINQ to SQL will generate this Employee class as a partial class, which we can use to extend this domain object’s behaviour. Let’s extend this class with an interface implementation for IDataErrorInfo.

public partial class Employee : IDataErrorInfo
{
    #region IDataErrorInfo Members

    public string Error
    {
        get { throw new NotImplementedException(); }
    }

    public string this[string columnName]
    {
        get { throw new NotImplementedException(); }
    }

    #endregion
}

IDataErrorInfo is an interface definition that is found in System.ComponentModel. It provides the functionality to offer custom error information that a user interface can bind to. Great, let’s do that! Assume we have a view which is used to edit this Employee object. The code for this will be quite easy: some HTML form stuff, Html.ValidationMessage calls, … Here’s a snippet:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Edit</h2>

    <%= Html.ValidationSummary() %>

    <% using (Html.BeginForm()) {%>
        <%= Html.Hidden("id", Model.EmployeeID) %>

        <fieldset>
            <legend>Fields</legend>
            <p>
                <label for="LastName">LastName:</label>
                <%= Html.TextBox("LastName") %>
                <%= Html.ValidationMessage("LastName", "*") %>
            </p>

            <!-- ... -->

        <fieldset>
    <% } %>

</asp:Content>

The controller’s action method for this will look like the following:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection)

    Employee employee = repository.RetrieveById(id);

    try
    {
        UpdateModel(employee, collection.ToValueProvider());
        repository.Save(employee);

        return RedirectToAction("Index");
    }
    catch
    {
        return View(employee);
    }
}

Nothing fancy here: a call to UpdateModel (to populate the Employee instance with data fom the form) and a try-catch construction. How will this thing know what’s wrong? This is where the IDataErrorInfo interface comes useful. ASP.NET MVC’s UpdateModel method will look for this interface implementation and retrieve information from it. The Error property that is defined on IDataErrorInfo returns a string containing any error that is “global” for the Employee object. The this[string columnName] indexer that is defined on IDataErrorInfo is used to retrieve error messages for a specific property. Now let’s make sure FirstName and LastName are provided:

public partial class Employee : IDataErrorInfo
{
    #region IDataErrorInfo Members

    public string Error
    {
        get { return ""; }
    }

    public string this[string columnName]
    {
        get
        {
            switch (columnName.ToUpperInvariant())
            {
                case "FIRSTNAME":
                    if (string.IsNullOrEmpty(FirstName))
                        return "Please provide a firstname.";
                    break;
                case "LASTNAME":
                    if (string.IsNullOrEmpty(LastName))
                        return "Please provide a lastname.";
                    break;
            }

            return "";
        }
    }

    #endregion
}

Great, let’s try it out. If I omit the firstname or lastname when editing an Employee object, here’s what the view looks like:

ASP.NET MVC form validation

How easy was that! More on the new things in the ASP.NET MVC release candidate can be found in ScottGu’s blog post.

kick it on DotNetKicks.com


Categories: ASP.NET | C# | General | MVC

Updated CarTrackr to ASP.NET MVC RC

image As you may have noticed, ASP.NET MVC 1.0 Release Candidate has been released over the night. You can read all about it in ScottGu’s blog post, covering all new tools that have been released with the RC.

Since I’ve been trying to maintain a small reference application for ASP.NET MVC known as CarTrackr, I have updated the source code to reflect some changes in the ASP.NET MVC RC. You can download it directly from the CodePlex project page at www.cartrackr.net.

Here’s what I have updated (copied from the release notes):

Specifying View Types in Page Directives

The templates for ViewPage, ViewMasterPage, and ViewUserControl (and derived types) now support language-specific generic syntax in the main directive’s Inherits attribute. For example, you can specify the following type in the @ Master directive:


<%@ Master Inherits="ViewMasterPage<IMasterInfo>" %>

An alternative approach is to add markup like the following to your page (or to the content area for a content page), although doing so should never be necessary.


<mvc:ViewType runat="server" TypeName="ViewUserControl&lt;ProductInfo>" />

The default MVC project templates for Visual Basic and C# views have been updated to incorporate this change to the Inherits attribute. All existing views will still work. If you choose not to use the new syntax, you can still use the earlier syntax in code.

ASP.NET Compiler Post-Build Step

Currently, errors within a view file are not detected until run time. To let you detect these errors at compile time, ASP.NET MVC projects now include an MvcBuildViews property, which is disabled by default. To enable this property, open the project file and set the MvcBuildViews property to true, as shown in the following example:


<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 

  <PropertyGroup> 

    <MvcBuildViews>true</MvcBuildViews> 

  </PropertyGroup>

Note: Enabling this feature adds some overhead to the build time.

You can update projects that were created with previous releases of MVC to include build-time validation of views by performing the following steps:

1. Open the project file in a text editor.

2. Add the following element under the top-most <PropertyGroup> element:


<MvcBuildViews>true</MvcBuildViews>

3. At the end of the project file, uncomment the <Target Name="AfterBuild"> element and modify it to match the following example:


<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'"> 

    <AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />

</Target>

kick it on DotNetKicks.com


Categories: ASP.NET | C# | General | MVC | Personal | Projects

Checking username availability with jQuery (screencast)

A new screencast has just been uploaded to the MSDN Belgium Chopsticks page. Don't forget to rate the video!

Checking username availability with jQuery

Abstract: "This screencast explains how to use jQuery and the ASP.NET MVC framework to quickly add a username availability checker to a user registration page."

kick it on DotNetKicks.com


CarTrackr on Windows Azure - Part 5 - Deploying in the cloud

This post is part 5 (and the final part) of my series on Windows Azure, in which I'll try to convert my ASP.NET MVC application into a cloud application. The current post is all about deploying CarTrackr in the cloud after all modifications done in previous posts.

Other parts:

Deploying CarTrackr on Azure

Deploying CarTrackr is done using the Azure developer portal. I'm creating a hosted service named "CarTrackr", which will host the cloud version of CarTrackr. I'm also creating a second storage acocunt project, used for TableStorage of all data in CarTrackr.

CarTrackr projects on Azure

To create a deployment package, the documentation states to right-click the Azure project in the CarTrackr solution, selecting "Publish...". I really hoped this would be easy. Unfortunately, this was some sort of a PITA... Great to see an Exception in the output window. Fortunately, someone had the same issue: Packaging Azure project manually when the 'Publish' action fails. The Exception seems to be thrown because the CarTrackr project is too big for the packaging system. *sigh* Starting an Azure SDK command-line and invoking the cspack.exe seemed to do the trick.

Anyway, on to uploading the generated package to Azure: the package, service configuration and deployment name should be given, after which an upload takes place.

Azure Services Developer Portal

Time to run! After browsing to http://cartrackr.cloudapp.net, I'm expecting to see CarTrackr!

CarTrackr running... NOT!

But no, Error 403 - Forbidden - Access is denied... What is wrong? My quest to host CarTrackr on Azure seems to be full of obstacles...

Deploying again!

After some hours of re-packing, re-building, re-deploying and starting to get annoyed, the solution was to change my call to cspack.exe... Not only the /bin folder should be packaged, but also all other content. My bad! Invoking C:\Users\Maarten\Desktop\CarTrackr>cspack "./CarTrackr_Azure/ServiceDefinition.csdef" /out:"./CarTrackr_Azure/CarTrackr_Azure.cspkg" /role:"Web;./CarTrackr"  was the right thing to do.

A deployment later, CarTrackr seemed to work smoothly!

CarTrackr on Azure!

And again...

Yes, again. I wrote this blog post about a week ago and decided to add Google Analytics in the live version of CarTrackr. While deploying, I decided to upload the latest version to the staging environment, and swap it with the production environment... Bad idea :-) Deployment totally killed my data storage (it seemed) and was throwing strange errors on the production URL.

Some hours later (that is: 2 hours of build, deploy, check, ..., a night sleep and some more build, deploy, check, ...), I had the strange idea of putting my production ServiceConfiguration data in the package itself. This should not be necessary, but you never know. And it is what I did with the first working production deployment. And yes, this indeed seemed to be the solution!

Check it out!

Checkout http://cartrackr.cloudapp.net for the live version. No need to sign up, only to sign in. Note: when entering a refuelling, enter the date in US English format... 

It is likely that you are going to download and play with my source code. When doing that, check out the TableStorage membership, role and session providers in the Azure SDK, which you can use in CarTrackr if you want. More on this can be found on http://blogs.msdn.com/jnak/archive/2008/11/10/asp-net-mvc-on-windows-azure-with-providers.aspx.

Looking for the Azure CarTrackr sources? CarTrackr_Azure.zip (654.98 kb)
Looking for the original CarTrackr sources? Checkout CodePlex.

Conclusion on Azure

It took me quite some hours to actually convert an existing application into something that was ready for deployment on Azure. The toughest parts were rewriting my repository code for TableStorage and getting the CarTrackr package deployed. With what I know know, I think writing an Azure application should not be more difficult than writing any other web application. The SDK provides everything you need, remember to check the samples directory for some useful portions of code.

I do have some notes though, which I hope will be used for future CTP's of Azure...

  • Your application is running fine on the development fabric?
    There's no 100% guarantee that the Azure web fabric will serve your application correctly when it runs smoothly on the development fabric. Just deploy and pray :-)
  • Viewing log files from the web fabric from the Azure Services Developer Portal would be a real time saver!
    Currently, you can copy the server logs to blob storage and then fetch the data from there, but a handy web-tool would really save some time when something goes wrong after deployment. Same story goes for TableStorage: it would be useful to have an interface to look at your data.
  • Packaging? Great, but... what if I only wanted to update one file in the application?
    I've uploaded my package about 10 times during deployment testing, which required 10 times... the full package size in bandwith! And all I wanted to do was modifying web.config...

Sidenote: funny to see that they are using OPC (Open Packaging Convention) for creating deployment packages. It shows how easy it is to create a custom file format with OPC, just like Julien Chable did for a photo viewer.

Overall, Microsoft is doing a good job with Azure. The platform itself seems reliable and stable, the concept is good. Perhaps they should consider selling the hosting platform itself to hosting firms around the globe, setting a standard for hosting platforms. Think of switching your hosting provider by simply uploading the package to another company's web fabric and modifying some simple configuration entries. Another idea: why not allow the packages developed for Azure to be deployed on any IIS server farm? 

kick it on DotNetKicks.com


Categories: ASP.NET | Windows Azure | C# | General | MVC

CarTrackr on Windows Azure - Part 4 - Membership and authentication

This post is part 4 of my series on Windows Azure, in which I'll try to convert my ASP.NET MVC application into a cloud application. The current post is all about implementing authentication in CarTrackr.

Other parts:

Picking a solution...

In my opening post on this series, I defined some tasks which I would probably have to do prior to being able to run CarTrackr on Azure. For membership and authentication, I defined 2 solutions:  Cloudship or Windows Live ID.

At first, Cloudship looked really nice as it is just an implementation of ASP.NET's provider model based on Azure. Some thinking cycles later, this did not feel right for CarTrackr... For CarTrackr, authentication only would be enough, membership would be real overkill.

The solution I'll be using in CarTrackr is Windows Live ID. Luckily, there's some ASP.NET MVC code for that in an older release of the MVC Membership Starter Kit.

Adding Live ID to CarTrackr

First of all, add the WindowsLiveLogin.cs class from the MVC Membership Starter Kit. Also ,ake sure it can configure itself by adding the Live ID settings in web.config:

<appSettings>
    <!-- See: http://msdn2.microsoft.com/en-us/library/bb676633.aspx and https://msm.live.com/app/default.aspx -->
    <add key="wll_appid" value="001600008000AF26"/>
    <add key="wll_secret" value="mvcmembershipstarterkit"/>
    <add key="wll_securityalgorithm" value="wsignin1.0"/>
</appSettings>

Now, I always like removing code. Actually, a lot of methods can be removed from the AuthenticationController due to the fact that Live ID will take care of lost password e-mails and stuff like that. After these stripping actions, my AccountController looks like the following:

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Web.UI;
using CarTrackr.Core;
using CarTrackr.Repository;
using CarTrackr.Models;
using CarTrackr.Filters;

namespace CarTrackr.Controllers
{

    [HandleError]
    [OutputCache(Location = OutputCacheLocation.None)]
    [LiveLogin]
    public class AccountController : Controller
    {
        private IUserRepository UserRepository;

        public AccountController()
            : this(null, null)
        {
        }

        public AccountController(IFormsAuthentication formsAuth, IUserRepository userRepository)
        {
            FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
            UserRepository = userRepository;
        }

        public IFormsAuthentication FormsAuth
        {
            get;
            private set;
        }

        [Authorize]
        public ActionResult Index()
        {
            return RedirectToAction("Login");
        }

        public ActionResult Login()
        {
            return View("Login");
        }

        public ActionResult Logout()
        {
            FormsAuth.SignOut();

            // Windows Live ID logout...
            HttpCookie loginCookie = new HttpCookie( "webauthtoken" );
            loginCookie.Expires = DateTime.Now.AddYears( -10 );
            Response.Cookies.Add( loginCookie );

            return RedirectToAction("Index", "Home");
        }

        protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (filterContext.HttpContext.User.Identity is WindowsIdentity)
            {
                throw new InvalidOperationException("Windows authentication is not supported.");
            }
        }

        #region Live ID

        public ActionResult WindowsLiveAuthenticate()
        {
            // initialize the WindowsLiveLogin module.
            WindowsLiveLogin wll = new WindowsLiveLogin(true);

            // communication channels
            HttpRequestBase request = this.HttpContext.Request;
            HttpResponseBase response = this.HttpContext.Response;

            // extract the 'action' parameter from the request, if any.
            string action = request["action"] ?? "";

            /*
              If action is 'logout', clear the login cookie and redirect
              to the logout page.

              If action is 'clearcookie', clear the login cookie and
              return a GIF as response to signify success.

              By default, try to process a login. If login was
              successful, cache the user token in a cookie and redirect
              to the site's main page.  If login failed, clear the cookie
              and redirect to the main page.
            */
            if (action == "logout")
            {
                return RedirectToAction("Logout");
            }
            else if (action == "clearcookie")
            {

                HttpCookie loginCookie = new HttpCookie("webauthtoken");
                loginCookie.Expires = DateTime.Now.AddYears(-10);
                response.Cookies.Add(loginCookie);

                string type;
                byte[] content;
                wll.GetClearCookieResponse(out type, out content);
                response.ContentType = type;
                response.BinaryWrite(content);
                response.End();
                return new EmptyResult();

            }
            else
            {
                WindowsLiveLogin.User wllUser = wll.ProcessLogin(request.Form);

                HttpCookie loginCookie = new HttpCookie("webauthtoken");
                if (wllUser != null)
                {
                    loginCookie.Value = wllUser.Token;

                    if (wllUser.UsePersistentCookie)
                    {
                        loginCookie.Expires = DateTime.Now.AddYears(10);
                    }
                }
                else
                {
                    loginCookie.Expires = DateTime.Now.AddYears(-10);
                }

                // check for user in repository
                CarTrackr.Domain.User user = UserRepository.RetrieveByUserName(wllUser.Id);
                if (user == null)
                {
                    user = new CarTrackr.Domain.User();
                    user.UserName = wllUser.Id;
                    UserRepository.Add(user);
                }

                // log user in
                response.Cookies.Add(loginCookie);
                FormsAuthentication.SetAuthCookie(user.UserName, false);

                return RedirectToAction("Login");
            }
        }

        #endregion
    }

    // The FormsAuthentication type is sealed and contains static members, so it is difficult to
    // unit test code that calls its members. The interface and helper class below demonstrate
    // how to create an abstract wrapper around such a type in order to make the AccountController
    // code unit testable.

    public interface IFormsAuthentication
    {
        void SetAuthCookie(string userName, bool createPersistentCookie);
        void SignOut();
    }

    public class FormsAuthenticationWrapper : IFormsAuthentication
    {
        public void SetAuthCookie(string userName, bool createPersistentCookie)
        {
            FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
        }
        public void SignOut()
        {
            FormsAuthentication.SignOut();
        }
    }
}

Yes, that's almost no code left compared to the original! Remember, Live ID will take care of all user-account-related stuff for me. The only thing I'm doing here is accepting the authentication ticket Live ID provides to CarTrackr. Yes, I'm actually registering the user on my cloud storage too, because I want to track how much users actually use CarTrackr...

One thing to notice: I've created an action filter attribute (hence the [LiveLogin] attribute on the AccountController class). The LiveLogin action filter looks like the following:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using CarTrackr.Core;
using CarTrackr.Models;

namespace CarTrackr.Filters
{
    public class LiveLogin : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            WindowsLiveLogin wll = new WindowsLiveLogin(true);

            SignInViewData viewData = new SignInViewData
            {
                AppId = wll.AppId,
                UserId = GetUserId(wll, filterContext.HttpContext.Request)
            };

            filterContext.Controller.ViewData["WindowsLiveLogin"] = viewData;
        }

        public static string GetUserId(WindowsLiveLogin wll, HttpRequestBase request)
        {
            HttpCookie loginCookie = request.Cookies["webauthtoken"];

            if (loginCookie != null)
            {
                string token = loginCookie.Value;

                if (!string.IsNullOrEmpty(token))
                {
                    WindowsLiveLogin.User user = wll.ProcessToken(token);

                    if (user != null)
                    {
                        return user.Id;
                    }
                }
            }

            return null;
        }
    }
}

What hapens in this code is actually checking for the Live ID context we are in. This context can be used in any view of CarTrackr since it is stored in the ViewData dictionary by this action filter: ViewData["WindowsLiveLogin"]. This context is used by a simple LiveIdControl (code in download later on) to render the Live ID sign in / sign out link.

Reminder for deployment...

One reminder left when deploying this to Azure: I'll have to make sure that Live ID posts the authentication ticket to the correct URL in CarTrackr. This can be done later in the Azure project management interface:

Live ID settings in Azure

Conclusion

This was a quite easy task compared to configuring TableStorage. Thank's to the MVC Membership Starter Kit, the Live ID integration was easy.

Stay tuned for the final part: deployment on Azure! I'll also provide a download link and a live link to the project.

kick it on DotNetKicks.com


Categories: ASP.NET | Windows Azure | C# | General | MVC

CarTrackr on Windows Azure - Part 3 - Data storage

This post is part 3 of my series on Windows Azure, in which I'll try to convert my ASP.NET MVC application into a cloud application. The current post is all about implementing cloud storage in CarTrackr.

Other parts:

Types of Azure storage

Windows Azure offers 3 types of cloud storage: blobs, tables and queues. Blob Storage stores sets of binary data, organized in containers of your storage account. Table Storage offers structured storage in the form of tables. The Queue service stores an unlimited number of messages, each of which can be up to 8 KB in size.

Windows Azure Storage Account

CarTrackr will use table storage to store cars and refuellings. Table Storage is one of the simplest way to store data in Azure. All tables are accessed using a Uri in the form of http://<applicationname>.tables.core.windows.net. You need to have a storage cccount if you want to use the table storage. Waiting for an invitation? The Azure SDK contains a development storage tool which simulates all cloud table storage features on your local host: http://127.0.0.1:10002

Each storage account has a Table containg an Entity. An Entity contains Columns. Entity can be considered to be the row and Columns as values. Each Entity always contains these properties: PartitionKey, RowKey and Timestamp . The PartitionKey and RowKey identify a row or an Entity. (source)

Implementing Azure TableStorage in CarTrackr

Configuring TableStorage

First of all, we have to specify we are going to use storage in the CarTrackr application. This can be achieved by adding some configuration to the CarTrackr_Azure's ServiceConfiguration.cscfg:

<?xml version="1.0"?>
<ServiceConfiguration serviceName="CarTrackr_Azure" xmlns=""http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration"">http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
  <Role name="Web">
    <Instances count="1" />
    <ConfigurationSettings>
      <Setting name="AccountName" value="devstoreaccount1"/>
      <Setting name="AccountSharedKey" value="Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw=="/>
      <Setting name="TableStorageEndpoint" value=""http://127.0.0.1:10002"/">http://127.0.0.1:10002"/>
    </ConfigurationSettings>
  </Role>
</ServiceConfiguration>

When deploying, we should of course change the TableStorageEndpoint to the Azure TableStorage endpoint. Account name and key should also be modified at that moment.

Development storage

Making sure all tables exist in TableStorage

To make sure all tables exist in TableStorage, a TableStorageDataServiceContext should be defined. This TableStorageDataServiceContext should contain some TableStorageEntity items. We can easily make all domain objects in CarTrackr of type TableStorageEntity. Make sure to build the samples in the Azure SDK folder and add a reference to the StorageClient.dll in that folder.

Creating a class that is TableStorage aware is easy now: inherit the Microsoft.Samples.ServiceHosting.StorageClient.Microsoft.Samples.ServiceHosting.StorageClient class, modify the constructor and you're done. I've also modified the domain classes, making RowKey the identifier for the current object.

namespace CarTrackr.Domain
{
    public partial class Car : TableStorageEntity, IRuleEntity
    {
        public Car()
            : base()
        {
            PartitionKey = Guid.NewGuid().ToString();
            RowKey = Guid.NewGuid().ToString();
        }

        // ... other code ...
    }
}

Also add the CarTrackrCloudContext class to the CarTrackr.Data namespace. This CarTrackrCloudContext class will implement a TableStorageDataServiceContext, defining which tables are in the TableStorage.

using System.Data.Services.Client;
using CarTrackr.Domain;
using Microsoft.Samples.ServiceHosting.StorageClient;

namespace CarTrackr.Data
{
    public class CarTrackrCloudContext : TableStorageDataServiceContext
    {
        public DataServiceQuery<Car> Cars
        {
            get
            {
                return CreateQuery<Car>("Cars");
            }
        }

        public DataServiceQuery<Refuelling> Refuellings
        {
            get
            {
                return CreateQuery<Refuelling>("Refuellings");
            }
        }

        public DataServiceQuery<User> Users
        {
            get
            {
                return CreateQuery<User>("Users");
            }
        }
    }
}

This CarTrackrCloudContext tells the application there are 3 types of tables, represented by 3 domain class types. Note that the class has been inherited from TableStorageDataServiceContext, which automatically connects to the TableStorage endpoint with the account information we stored previously in the Service Configuration file.

To make sure tables exist and are up to date when the application is started, add the following to CarTrackr's Global.asax:

protected static bool tablesRegistered = false;
protected static object syncLock = "";

protected void Application_BeginRequest(object sender, EventArgs e)
{
    if (!tablesRegistered)
    {
        lock (syncLock)
        {
            if (!tablesRegistered)
            {
                try {
                    StorageAccountInfo account = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration();
                    TableStorage.CreateTablesFromModel(typeof(CarTrackr.Data.CarTrackrCloudContext), account);
                    tablesRegistered = true;
                }
                catch { }
            }
        }
    }
}

The TableStorage client will create all requried tables based on the CarTrackrCloudContext class's IQueryable properties. Note that I'm using double-check locking here to make sure tables are only created once (performance). 

Querying data

Luckily, my repository code is not subject to much changes. Linq queries just keep working on the Azure TableStorage. Only Insert, Update and Delete are a little bit different. The CarTrackrCloudContext class represents the runtime context of ADO.NET Data Services and enables to use AddObject(), DeleteObject(), UpdateObject(), followed by SaveChanges().

Here's an example on adding a Car (CarRepository):

public void Add(Car car)
{
    if (car.OwnerId == Guid.Empty)
        car.OwnerId = User.UserId;

    car.EnsureValid();

    DataSource.DataContext.AddObject("Cars", car);
    DataSource.DataContext.SaveChanges();
}

Storage conclusions

I have actually modified my Linq to SQL classes and repository code as TableStorage currently only supports Binary, Bool, DateTime, Double, Guid, Int, Long and String. No decimals and custom types anymore... There's also some other missing features regarding ordering of data and joins which required me to change a lot of repository code. But hey, it's still only the repository code I needed to change!

Next post will be about membership and authentication. Stay tuned!

kick it on DotNetKicks.com


Categories: ASP.NET | Windows Azure | C# | General | MVC

CarTrackr on Windows Azure - Part 2 - Cloud-enabling CarTrackr

This post is part 2 of my series on Windows Azure, in which I'll try to convert my ASP.NET MVC application into a cloud application. The current post is all about enabling the CarTrackr Visual Studio Solution file for Windows Azure.

Other parts:

Adding CarTrackr_WebRole

For a blank Azure application, one would choose the Web Cloud Service type project (installed with teh Azure CTP), which brings up two projects in the solution: a <project> and <project>_WebRole. The first one is teh service definition, the latter is the actual application. Since CarTrackr is an existing project, let's add a new CarTrackr_Azure project containing the service definition.

Right-click the CarTrackr solution and add a new project. From the project templates, pick the "Cloud Service -> Blank Cloud Service" item and name it "CarTrackr_Azure". The CarTrackr_Azure project will contain all service definition data used by Windows Azure to determine the application's settings and environment.

 Creating CarTrackr_Azure

CarTrackr solution

Great! My solution explorer now contains 3 projects: CarTrackr, CarTrackr.Tests and the newly created CarTrackr_Azure. Next thing to do is actually defining the CarTrackr project in CarTrackr_Azure as the WebRole project. Right-click "Roles", "Add", and notice... we can not promote CarTrackr to a WebRole project. Sigh!

Edit the CarTrackr.csproj file using notepad and merge the differences in (ProjectTypeGuids, RoleType and ServiceHostingSDKInstallDir):


<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>

    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>

    <ProductVersion>9.0.30729</ProductVersion>

    <SchemaVersion>2.0</SchemaVersion>

    <ProjectGuid>{E536FB25-134E-4819-9BAC-0D276D851FB8}</ProjectGuid>

    <ProjectTypeGuids>{603c0e0b-db56-11dc-be95-000d561079b0};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

    <OutputType>Library</OutputType>

    <AppDesignerFolder>Properties</AppDesignerFolder>

    <RootNamespace>CarTrackr</RootNamespace>

    <AssemblyName>CarTrackr</AssemblyName>

    <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>

    <RoleType>Web</RoleType>

    <ServiceHostingSDKInstallDir Condition=" '$(ServiceHostingSDKInstallDir)' == '' ">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\ServiceHosting\v1.0@InstallPath)</ServiceHostingSDKInstallDir>

  </PropertyGroup>

  <!-- ... -->

</Project>

Visual Studio will prompt to reload the project, allow this by clicking the "Reload" button. Now we can right-click "Roles", "Add", "Web Role Project in Solution" and pick CarTrackr. Note that the 2 files in CarTrackr_Azure now have been updated to reflect this.

ServiceDefinition.csdef now contains the following:


<?xml version="1.0" encoding="utf-8"?>

<ServiceDefinition name="CarTrackr_Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">

  <WebRole name="Web">

    <InputEndpoints>

      <InputEndpoint name="HttpIn" protocol="http" port="80" />

    </InputEndpoints>

  </WebRole>

</ServiceDefinition>

This file will later instruct the Azure platform to run a website on a HTTP endpoint, port 80. Optionally, I can also add a HTTPS endpoint here if required. For now, this definition wil do.

ServiceConfiguration.csdef now contains the following:


<?xml version="1.0"?>

<ServiceConfiguration serviceName="CarTrackr_Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">

  <Role name="Web">

    <Instances count="1" />

    <ConfigurationSettings />

  </Role>

</ServiceConfiguration>

This file will inform Azure of the required environment for CarTrackr. First of all, one instance will be hosted. If it becomes a popular site and more "servers" are needed, I can simply increase this number and have more power in the cloud. The ConfigurationSettings element can contain some other configuration settings, for example where data will be stored. I think I'll be needing this in a future blog post, but for now, this will do.

 

Service Configuration

It's in the cloud!

After doing all configuration steps, I can simply start the CarTrackr application in debug mode.

CarTrackr in the cloud!

Nothing fancy here, everything still works! I just can't help the feeling that Windows Azure will not know my local SQL server for data storage... Which will be the subject of a next blog post!

kick it on DotNetKicks.com


Categories: ASP.NET | Windows Azure | C# | General | MVC

Track your car expenses in the cloud! CarTrackr on Windows Azure - Part 1 - Introduction

As you may see in the title, I will be starting a series on modifying my CarTrackr sample application to a cloud-based, Windows Azure application. At this point, I don't know if it's easy nor do I know what it takes to achieve this goal. I only have some assumtions on how CarTrackr can be converted to a cloud application.

This post is part 1 of the series, in which I'll describe the architecture of Windows Azure and what I think it takes to convert my ASP.NET MVC application into a cloud application.

Other parts:

Microsoft Azure

Azure Services PlatformAt Microsoft PDC 2008, the Azure Services Platform was announced in the opening keynote. Azure is the name for Microsoft’s Software + Services platform, an operating system in the cloud providing services for hosting, management, scalable storage with support for simple blobs, tables, and queues, as well as a management infrastructure for provisioning and geo-distribution of cloud-based services, and a development platform for the Azure Services layer.

You can currently download the Windows Azure SDK from www.azure.com and play with it on your local computer. Make sure to sign-up at the Azure site: you might get lucky and receive a key to test the real thing.

CarTrackr

From my previous blog post: "CarTrackr is a sample application for the ASP.NET MVC framework using the repository pattern and dependency injection using the Unity application block. It was written for various demos in presentations done by Maarten Balliauw. CarTrackr is an online software application designed to help you understand and track your fuel usage and kilometers driven."

CarTrackr, cloud version That being said: what will it take to port this onto the Azure platform? First of all, a new logo applies. I now want a logo with clouds in it. Since it's still no official release, I'll also keep the "beta" label in place. Looks nice, eh? :-)

Seriously, here's what I think needs to be done:

Concept Current implementation Azure implementation
Data store Repository pattern on top of Linq to SQL. Repository pattern (whew!) on top of Azure TableStorage.
Membership ASP.NET membership Windows Live ID or Cloudship

In addition to the above table, I'll also have to make the CarTrackr solution aware of Azure. Next thing: make Azure aware of ASP.NET MVC... I'll also have to deploy this application in the cloud at the end. Stay tuned!

kick it on DotNetKicks.com


Categories: ASP.NET | Windows Azure | C# | General | MVC

ASP.NET MVC XForms released on CodePlex

Just noticed there's a new project on CodePlex related to the ASP.NET MVC framework: MVC XForms. MVC XForms is a simple UI framework for ASP.NET MVC based on the W3C XForms specification. It provides a set of form controls that allow updating of complex model objects.

Picked these project goals from Jon Curtis' blog:

  • To allow automatic form population, deserialization and validation based on the (arbitrarily complex) model.
  • To produce semantic HTML forms using the logic of XForms.
  • To output clean, terse HTML.
  • No javascript, unless necessary and always optional and unobtrusive.
  • To enable clean, terse view code.
  • To make the framework as extensible and customisable as possible without compromising simplicity or the above goals.
  • Use convention over configuration and a fluent API.

Great story, but how does it work?

I haven't gone into any advanced scenario's, but have instead used a simple case to demonstrate some of the MVC XForms basics. First of all, I've created a Person class with an Id (int), Name (string) and BirthDate (DateTime). This class is used by a specific view in my application, of which the view markup looks like this:

<% Html.XForm(ViewData.Model).Form(form => { %>
  <%=form.Input(p => p.Name).Label("Name") %>
  <%=form.Input(p => p.BirthDate).Label("Birth date") %>
<% }); %>

This is all there is to creating an MVC XForm. Note that I'm creating a XForm based on my model, and that I want an input field for the Name and BirthDate properties, each with a label supplied.

On to rendering: the HTML generated by MVC XForm looks like the following:

<form action="" method="post" class="xf xform">
  <div class="xf input text">
    <label for="Name">Name</label>
    <input id="Name" type="text" name="Name" value="Maarten Balliauw"/>
  </div>
  <div class="xf input date">
    <label for="BirthDate">Birth date</label>
    <input id="BirthDate" type="text" name="BirthDate" value="21-12-1983"/>
  </div>
</form>
<script type="text/javascript">
  $(document).ready(function(){mvc.xforms.init({"id":"","cons":[]})});
</script>

That's actually nice, clean HTML markup! Note the CSS classes that are applied on certain fields. For example, the div element for BirthDate has a CSS class "date", which can be used by, for example, jQuery to enable a date picker on that field.

More information

In the CodePlex download (http://www.codeplex.com/mvcxforms), there is a sample project which makes use of all simple and advanced ideas in MVC XForms. You can also check out Jon Curtis' blog posts on MVC XForms to read some more samples.

kick it on DotNetKicks.com


Using the ASP.NET MVC ModelBinder (screencast)

A new screencast has just been uploaded to the MSDN Belgium Chopsticks page. Don't forget to rate the video!

Using ASP.NET MVC ModelBinder

Abstract: "This screencast demonstrates how code can be made more maintainable and testable by delegating binding to client data to the ASP.NET MVC model binder architecture."

kick it on DotNetKicks.com