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:
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:
-
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. - 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. -
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. - 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
The source code can be found at LiteDispatch
GitHub repository and the latest source code is available
here
Build A Website
Create an account in GitHub and then install GitHub for Windows
Then, from the GitHub client, add a new repository and publish it:
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:
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:
Now, you need to enter your website name and your preferred Azure region, for example:
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:
Then select the repository and the branch that you want to deploy to the Azure WebSite:
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:
Now you should be able to open the website in your browser:
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.
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
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.
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);
}
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)
{
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())
{
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
WebSecurity.InitializeDatabaseConnection("SecurityDb", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
catch (Exception ex)
{
...
}
}
}
}
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:
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:
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.
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:
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
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:
If the dispatch note is confirmed, then the user is taken to the active dispatch
screen:
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:
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