Click here to Skip to main content
Click here to Skip to main content

LiteDispatch - Logistic solution on the cloud

By , 13 May 2013
 

Please note

This article is an entry in our Windows Azure Developer Challenge. Articles in this sub-section are not required to be full articles so care should be taken when voting.

Introduction 

Developing applications for the Microsoft Azure platform is becoming increasingly easy these days. It is relatively inexpensive for a development team in terms of effort and money to develop an application in this sort of environment; start costs have continuously decreased over the last couple years and currently it is very straightforward to put together a full functional prototype application with no or little deployment cost at all. Also the new integration features like the one with GitHub provide a fantastic set of tools for a team; tasks like configuring automated builds and deployments to the public web site is a question of minutes.

This article discusses the benefits for a development team using the Azure platform, it covers topics like integration with GitHub, automatic build and deployment; data persistence options from using SQL-CE, SQL Express and SQL Azure with EF or/and Nhibernate; development of AzureWebSites and Azure Mobile Services.  

Windows Azure Challenge

Current sections available for each of the challenge phases:

First Challenge Getting Started April 15 - May 3
Second Challenge Build A Website April 29 - May 12
Second Challenge - Spot Prize LiteDispatch Easter Egg April 29 - May 12

Background

I have recently worked in a proof of concept project for a client that may be well a very common scenario for many of you. The client is responsible for the management of dispatches of a very large food shopping centre; they receive large deliveries on daily basis from many different hauliers. The goods are then delivered to the individual shops and at the end of each day a final delivery dispatch must be produced for each of the shops to pay for the daily delivery. 

Currently the client only has a financial package system installed for accounting purposes but the rest of the process is done using paper. The client is looking for a system to help them on the delivery side that would enhance the production of shop delivery notes and the automatic feed to their financial system.

Some of the aspects that favored to decide for an application on the cloud were the following:

  • The hauliers should produce daily delivery listings, a web application would facilitate the input of those listings by the hauliers themselves.  
  • Tablets would be assigned to employees for listing validation and sign off of the shop delivery notes. Again an on-line application was preferred than installing a wire-less network to support mobile users.   

The Windows Azure  platform seemed to be a more adequate solution and cost effective. It eliminates the need for the client to acquire new expertises and it reduces the upfront cost of the project. Azure provides a very reliable service and integrates very well with the tools used by the project development team. A prototype was successfully developed in very little time for demo purposes with no cost whatsoever. As well, the development team was based in a different country that the client, so having a cloud based platform proved very productive in terms of turning around enhancements and defects. 

This article example application, LiteDispatch, is based on this project. 

Roadmap

I am planning the following stages in the execution of the article so aligns to the challenge schedule: 

  1. Build Web Site Prototype
    At this stage a ASP MVC application should be available with some basic functionality to demonstrate the workflow for a haulier to input dispatch details; it would be a prototype so at this point we focus in setting the environment using WebSites in Azure, integrate to a GitHub repository and get some UI functionality in place. It would also discuss how to implement simple authentication using EF and SQL-CE. 
  2. Integrate to SQL Azure
    This phase discusses about setting a Business Domain and develop a persistence layer using EF. In first instance it would cover integration with SQL-CE and at the end of the article it would demonstrate how to move the database to SQL Azure.  
  3. Virtual Machines
    This is a new aspect for me, I never use it before in Azure. Couple ideas would be to install SQL-Express and then to see what it takes to integrate it with the Web App. I am also interested in the generation of reports in PDF format, it might well be the ideal problem to resolve using Virtual Machines. 
  4. Mobile Access
    Two aspects to cover at this stage; firstly, ensure that the web application works correctly in a table.  The second aspect is to integrate a server email notification each time a haulier creates a new delivery list.  

Solution Artifacts and More

The LiteDispatch is deployed on the following Azure website:
http://litedispatch.azurewebsites.net/
To use the site you need the following credentials for the login screen:
user: codeproject
pwd: litedispatch

LiteDispatch Login Screen

The source code can be found at LiteDispatch GitHub repository and the latest source code is available here

Build A Website

Web Site Infrastructure

Create A GitHub Repository

Create an account in GitHub and then install GitHub for Windows

Then, from the GitHub client, add a new repository and publish it:
Create a new repo in GitHub

Now, you just need to copy the projects to your local repository, the attached source code in this article might be used for testing purposes. Once you have the application running locally, commit the changes to GitHub:
Publish changes to GitHub

Create An Azure WebSite Integrated With GitHub

You need to have an account in Azure, if you don't have one then you might want to check out at for the free trial. Once you are in the portal, create a new website by selecting New>WebSite>QuickCreate:
Create a new repo in GitHub

Now, you need to enter your website name and your preferred Azure region, for example:
Enter region details

Once the website is created, go back to the home website page and select "Set up deployment from source code control", then select GitHub, it would request your GitHub credentials:
Select GitHub

Then select the repository and the branch that you want to deploy to the Azure WebSite:
Select GitHub Repository

At this stage, Azure automatically deploys the latest source code, if everything goes well you should see something like the following under the deployment tab:
Sucessful deploymentt

Now you should be able to open the website in your browser:
LiteDispatch Login screen

At this stage, if you make any code changes and push the commit to GitHub, you can see within a couple minutes the latest version being deployed in Azure. Not too bad, it probably takes less than 10 minutes to create a free trial account and have your web site running.

Technologies Deployed

The LiteDispatch at this stage uses the following technologies:

  • ASP MVC 4
  • Forms Authentication using simple membership and role providers
  • Entity Framework configured to connect to SQL-CE

ASP MVC4 and Azure

I used the Visual Studio MVC4 template to create the LiteDispatch project as it can be noticed on the screenshots, everything in this template seems to work out fine when deployed on Azure. As mentioned before, the solution has the NuGet package restore enabled which works fine in Azure without any additional configuration settings, this is a bonus as it reduces deployment times enormously and the size of the repository as well.

Authentication - Simple Membership

For authentication purposes, I used the Simple Membership in MVC 4. In this way hauliers need to obtain a user and password before they can gain access to the site. A haulier role was created for this sort of users as well.
The default MVC4 Template in Visual Studio is set to use the Simple Membership by default, the web.config is configured so it sets the role and membership managers as follows:

        ...
            <roleManager enabled="true" defaultProvider="SimpleRoleProvider">
            <providers>
            <clear />
            <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData" />
            </providers>
        </roleManager>
        <membership defaultProvider="SimpleMembershipProvider">
            <providers>
            <clear />
            <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
            </providers>
        </membership>
        </system.web>                       

The authentication is set to Forms in the web.config:

    <authentication mode="Forms">
        <forms loginUrl="~/Account/Login" timeout="2880" />
    </authentication>         

In the Account controller the Login logic is very straight forward:

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Login(LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
        {
        return RedirectToLocal(returnUrl);
        }

        // If we got this far, something failed, redisplay form
        ModelState.AddModelError("", "The user name or password provided is incorrect.");
        return View(model);
    }

The WebSecurity class is part of the WebMatrix.WebData assembly that manages the Simple membership.

One aspect to notice is the filter applied to the controller: InitializeSimpleMembership. It ensures that the EF context is initialised:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute
    {
    private static SimpleMembershipInitializer _initializer;
    private static object _initializerLock = new object();
    private static bool _isInitialized;

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Ensure ASP.NET Simple Membership is initialized only once per app start
        LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
    }

    private class SimpleMembershipInitializer
    {
        public SimpleMembershipInitializer()
        {
        Database.SetInitializer<UsersContext>(null);

        try
        {
            using (var context = new UsersContext())
            {
            if (!context.Database.Exists())
            {
                // Create the SimpleMembership database without Entity Framework migration schema
                ((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
            }
            }

            WebSecurity.InitializeDatabaseConnection("SecurityDb", "UserProfile", "UserId", "UserName", autoCreateTables: true);
        }
        catch (Exception ex)
        {
            ...
        }
        }
    }
    }

Entity Framework and SQL-CE

Currently only the Authentication entities are persisted to the back-end database, the rest of the business instances are not yet in place and instead the screens are using mocked MVC Models instances.
The solution is using EF Code First to manage the persistence and CRUD operations of the UserProfile entity which is declared at the AccountModel.cs file:

    [Table("UserProfile")]
    public class UserProfile
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int UserId { get; set; }
        public string UserName { get; set; }
        public string EmailAddress { get; set; }
    }

The EmailAddress field is an extended property from the default model so you can see how you can extend the model if required. For example, a property could have been added to provide a link between a user and the haulier that works for.
An EF DbContext is also declared in this file:

    public class UsersContext : DbContext
    {
    public UsersContext()
        : base("LiteDispatch.Security")
    {
    }

    public DbSet<UserProfile> UserProfiles { get; set; }
    }

Notice that the passed connection string matches to the one declared on the web.config:

    <connectionStrings>
        <add name="SecurityDb" providerName="System.Data.SqlServerCe.4.0" connectionString="Data Source=|DataDirectory|\LiteDispatch.Security.sdf;default lock timeout=60000" />
    </connectionStrings>

Besides the Entity Framework default packages, the following were added to the solution so it can use a SQL-CE database:

Required NuGet packages to get EF to work with SQL-CE

Once the above packages are installed the web.config is modified to work with SQL-CE as follows:

        <entityFramework>
        <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
            <parameters>
            <parameter value="System.Data.SqlServerCe.4.0" />
            </parameters>
        </defaultConnectionFactory>
        </entityFramework>
        <system.data>
        <DbProviderFactories>
            <remove invariant="System.Data.SqlServerCe.4.0" />
            <add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
        </DbProviderFactories>
        </system.data>

You may also want to install the Visual Studio SQL-CE Toolbox extension:

SQL-CE Toolbox for VS

We are going to cover in great detail the persistence aspects of the application in the next article, but it is worth mentioning that EF Migrations were enable and the database is pre-populate with a couple users: admin and codeproject.

LiteDispatch Functionality

The following functions are available at this stage:

  • User Authentication
  • New Dispatch Note
  • Dispatch Enquiry
  • Dispatch Detail View
  • Dispatch Detail Print
  • Release Notes

User Authentication

We have already covered this aspect in the previous section but there is one aspect to mention: how to generate the database from scratch. The project has the EF Migrations setting enable, we can use the Update-Database -force command on the Package Management Console:

PM Console Menu

This is handy if you want to create the database and table schema, it also performs a database seed that you can customised. In the LiteDispatch case the Configuration class is used when the Update command is invoked, it creates two roles: haulier and administrator and two users: admin and codeproject:

internal sealed class Configuration : DbMigrationsConfiguration<UsersContext>
{
public Configuration()
{
    AutomaticMigrationsEnabled = true;
}

protected override void Seed(UsersContext context)
{
    WebSecurity.InitializeDatabaseConnection(
    "SecurityDb",
    "UserProfile",
    "UserId",
    "UserName", autoCreateTables: true);

    CreateAdminUser();
    CreateHaulierUser();
}

private void CreateAdminUser()
{
    if (!Roles.RoleExists("Administrator")) Roles.CreateRole("Administrator");

    if (!WebSecurity.UserExists("admin"))
    {
    WebSecurity.CreateUserAndAccount(
        "admin",
        "password",
        new { EmailAddress = "admin.staff@lite.dispatch.com" });
    }

    if (!Roles.GetRolesForUser("admin").Contains("Administrator"))
    {
    Roles.AddUsersToRoles(new[] { "admin" }, new[] { "Administrator" });
    }
}

private void CreateHaulierUser()
{
    if (!Roles.RoleExists("Haulier")) Roles.CreateRole("Haulier");

    if (!WebSecurity.UserExists("codeproject"))
    {
    WebSecurity.CreateUserAndAccount(
        "codeproject",
        "litedispatch",
        new { EmailAddress = "bluewhale.staff@lite.dispatch.com" });
    }

    if (!Roles.GetRolesForUser("codeproject").Contains("Haulier"))
    {
    Roles.AddUsersToRoles(new[] { "codeproject" }, new[] { "Haulier" });
    }
}
}

New Dispatch Note

New Dispatch Form

In this screen the user enters a new dispatch by uploading an excel file that conforms to a given format. A template of this file is available on the screen at the "DownLoad Template" link. Currently the file content is not checked at all as the data is mocked, but the application still checks that the file type is the correct one, as a result this screen only works correctly if the local machine recognises XLSX files.

Once the file is uploaded, an screen is displayed so the user can verify the imported details:

Confirm Dispatch

If the dispatch note is confirmed, then the user is taken to the active dispatch screen:

Active Dispatches

From this screen the user can drill down to see the dispatch details or print it.

Challenge 2 Spot Prize: Easter Egg

Find the LiteDispatch easter egg at the login page, click on the CodeProject logo to see bobs appearing, the more you click the more bobs:

easter_egg

I have used the HTML5 Canvas functionality in conjuction with the KineticJS library. It is the first time I use it, the documentation is good, it was not a question of five minutes to get the animation working but I am happy with the result.

The code can be found at the login.cshtml file, the following is the javascript that gets the animation working:

    <script defer="defer" type="text/javascript">
        var stage = new Kinetic.Stage({
            container: 'kinectContainer'
            
        });
        var layer = new Kinetic.Layer();        
        stage.add(layer);

        function addBob() {
            $('#kinectContainer')
                .css("position", "absolute")
                .css("bottom", "0")
                .css("top", "0")
                .css("left", "0")
                .css("right", "0");

            stage.setWidth ($('#kinectContainer').width());
            stage.setHeight($('#kinectContainer').width());
            
            var imageObj = new Image();
            imageObj.src = '@Url.Content("../../Images/bob.png")';

            var period = 8000;
            var randX = Math.random() * 2 - 1;
            var randY = Math.random() * 2 - 1;
            var centerX = stage.getWidth() / 2;
            var amplitudeX = stage.getWidth() / 2 * randX;
            var centerY = stage.getHeight() / 2;
            var amplitudeY = stage.getHeight() / 2 * randY;
            imageObj.onload = function () {
                var bob = new Kinetic.Image({
                    x: 0,
                    y: stage.getHeight() / 2,
                    image: imageObj,
                    width: 64,
                    height: 88,
                    name: 'bob'
                });
                layer.add(bob);
                var rotDeg = Math.random();
                var animBob = new Kinetic.Animation(function (frame) {
                    bob.setX(amplitudeX * Math.sin(frame.time * 2 * Math.PI / period) + centerX);
                    bob.setY(amplitudeY * Math.sin(frame.time * 2 * Math.PI / period) + centerY);
                    bob.rotateDeg(rotDeg);
                }, layer);

                animBob.start();
            };
        }

        $('#moreBobs').click(addBob);

An aspect that resulted more problematic that I envisaged was to set the div for the Canvas in a full screen mode. I am not sure why but CSS was not working for me so at the end I had to modify the div using jQuery as it can be seen in the code above.

History

12-May-2013 - Second Challenge Spot - Easter Egg effect was added to the login screen
07-May-2013 - Second Challenge section was added to the article: Build A Website
06-May-2013 - Project wins Challenge 1 award
05-May-2013 - Azure Web Site article was added to the series
25-Apr-2013 - Article was created


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Enrique Albert
Software Developer (Senior)
Ireland Ireland
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralNeat Easter Egg!protectorAspDotNetDev12 May '13 - 16:10 
GeneralRe: Neat Easter Egg!professionalEnrique Albert15hrs 29mins ago 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130513.1 | Last Updated 13 May 2013
Article Copyright 2013 by Enrique Albert
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid