(This is cross-posted on the 10th Magnitude blog.)
In
my last post, I talked about using
Azure web sites to save beaucoup bucks over
Azure Cloud Services web roles on nonessential, internal, and development
web applications. In this post I'll go over a couple of things that bit me in the
course of deploying a bunch of applications to Azure web sites in the last two weeks.
First, let me acknowledge that engineering a .NET application to support both types
of deployment is a pain. Azure web sites can't use Cloud Storage, including tables,
blobs, and queues. You'll need to architect (or refactor) your application to get
its data from different sources based on whether it's running inside Cloud Services
or not.
Once you've done all that (simple, right?), one line of code will let the application
know where it is:
if (RoleEnvironment.IsAvailable)
{ DoCloudServicesStuff(); }
But here's the catch. Azure web sites don't have access to the RoleEnvironment class by default. The class lives in
the Microsoft.WindowsAzure.ServiceRuntime
assembly, which Cloud Services applications get through their VM's GAC and which
website applications do not.
So even for your application to determine whether it's running in Cloud Services
or not, you'll have to add two assemblies to the website project file: Microsoft.WindowsAzure.ServiceRuntime.dll and msshrtmi.dll. If you don't, you'll get one of these
runtime errors:
Could not load file
or assembly 'Microsoft.WindowsAzure.ServiceRuntime, Version=1.7.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find
the file specified.
Could not load file
or assembly 'msshrtmi, Version=1.7.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
or one of its dependencies. The system cannot find the file specified.
(On my computer, the ServiceRuntime assembly is in "C:\Program Files\Microsoft SDKs\Windows
Azure\.NET SDK\2012-06\ref" and msshrtmi is in "C:\...\2012-06\bin\runtimes\base\x86\".)
Once you have added those two references to your web project, find them in the References
list under the project root, select both, right-click, and open the Properties tab.
Then set both of them to Copy Local = true, like this:
This tells the Publisher to include them in the deployment to Azure web sites. Note
that you'll have to include these in your web application if any of the satellite
assemblies uses the RoleEnvironment class (like our time zone handler, for example).
But now you have a new problem if you have dual deployments: you need two web project
files, because the Cloud Services deployment will crash when the web role starts
if you have those two assemblies referenced explicitly. And if you have two different
web application project files, you'll need two solution files as well.
Of course, it only takes about two minutes to:
- Make a copy of the solution file (usually "MyApplication-WebRole.sln"
or "MyApplication-Website.sln" for the new copy);
- Make a copy of the Web project file (you don't need different versions of your
other assembly projects);
- Manually edit the new solution file to point to the new project file.
(You could, I suppose, accomplish the same thing with build scripts and lots of
conditional compile regions, but I can't call that an improvement. Like I said,
this approach has more PITA than a falafel stand.)
We also find it helpful to wait until late in the development cycle to create the
second solution/project pair of files, because you'll need to keep the two project
files identical except for the two assembly references.
You have to do one more thing, unfortunately. You need to build your website deployment
for the x86 platform target:
The msshrtmi assembly requires explicitly building the application for x86 or x64
deployments, depending on whether you grab it from "C:\Program Files\Microsoft SDKs\Windows
Azure\.NET SDK\2012-06\bin\runtimes\base\x86\" or "...\x64\". But we've
run into other problems trying to use the x64 version, so we don't recommend it.