EPiServer Azure Virtual Path Provider, part two

This is the second blog post about creating a virtual path provider for EPiServer CMS that can be used together with the Windows Azure platform. It’s the fifth installment in the “Running EPiServer on the Windows Azure Platform” series.

Azure Virtual Path Provider

47538_4847I assume you’ve read part one. So let’s get right to it! Since we cannot specify the drive letter of our cloud drive in episerver.config we’ll make two adjustments to the <providers> <add>-element.

  • Add the parameter cloudVhdFileName
  • Replace the drive letter with the string [CloudDynamicDriveLetter]

The parameter cloudVhdFileName will be used to identify which virtual drive the current VPP should get its drive letter from. The [CloudDynamicDriveLetter] string will be replaced with the dynamically assigned drive letter from Windows Azure.

Here’s how it should look when you’re done. Note that the type has changed from the standard EPiServer versioning provider. :)

<virtualPath customFileSummary="~/FileSummary.config">
    <providers>
        <clear />
        <add showInFileManager="false" virtualName="Page Files" virtualPath="~/PageFiles/" bypassAccessCheck="false" name="SitePageFiles" type="DBLOG.Azure.AzureVirtualPathProvider.AzureVirtualPathVersioningProvider,DBLOG.Azure.AzureVirtualPathProvider" indexingServiceCatalog="Web" cloudVhdFileName="EPiServerVPP.vhd" physicalPath="[CloudDynamicDriveLetter]\VPP\PageFiles" />
        <add showInFileManager="true" virtualName="Global Files" virtualPath="~/Global/" bypassAccessCheck="false" name="SiteGlobalFiles" type="DBLOG.Azure.AzureVirtualPathProvider.AzureVirtualPathVersioningProvider,DBLOG.Azure.AzureVirtualPathProvider" indexingServiceCatalog="Web" cloudVhdFileName="EPiServerVPP.vhd" physicalPath="[CloudDynamicDriveLetter]\VPP\Global" />
        <add showInFileManager="true" virtualName="Documents" virtualPath="~/Documents/" bypassAccessCheck="false" maxVersions="5" name="SiteDocuments" type="DBLOG.Azure.AzureVirtualPathProvider.AzureVirtualPathVersioningProvider,DBLOG.Azure.AzureVirtualPathProvider" cloudVhdFileName="EPiServerVPP.vhd" physicalPath="[CloudDynamicDriveLetter]\VPP\Documents" />
</providers> <filters /> </virtualPath>

Now add a class library project to the solution. I named mine DBLOG.Azure.AzureVirtualPathProvider. Add a reference to it in your ASP.NET WebRole-project. We’re going to inherit from the standard EPiServer VirtualPathVersioningProvider class. Now here’s where it gets tricky. Since we’re implementing a base type (VirtualPathVersioningProvider) that isn’t actually meant to be derived from we have no possibilty – save heavy use of reflection – to modify how the base constructor behaves. I admit that I did have a go with reflection and got it to semi-work – but it was messy. Instead let’s create a static method that alters the configuration parameters that are retrieved from EPiServer.config and modify them before they are passed to the base constructor.

public class AzureVirtualPathVersioningProvider : VirtualPathVersioningProvider
{
    public AzureVirtualPathVersioningProvider(string name, NameValueCollection configurationParameters)
        : base(name, UpdateToCloudDriveLetters(configurationParameters))
    {
        // Nothing happens in the constructor of our derived class. Instead we run the UpdateToCloudDriveLetters 
        // method on the configurationParameters as they are passed to the base constructor.
    }

    /// <summary>
    /// Updates the drive letters of the physicalPath-parameter to reflect the drive letter dynamically assigned by Azure
    /// </summary>
    private static NameValueCollection UpdateToCloudDriveLetters(NameValueCollection configurationParameters)
    {
        string cloudVhdFileName = configurationParameters["cloudVhdFileName"] as string;

        string physicalPath = configurationParameters["physicalPath"] as string;

        if (String.IsNullOrEmpty(cloudVhdFileName))
            throw new ArgumentException("Parameter cannot be null or empty!", "cloudVhdFileName");

        if (String.IsNullOrEmpty(physicalPath))
            throw new ArgumentException("Parameter cannot be null or empty!", "physicalPath");

        // Set the new physical path with the correct Azure drive letter
        configurationParameters["physicalPath"] = ResolveCloudDriveLetter.Resolve(configurationParameters["physicalPath"], cloudVhdFileName);

        return configurationParameters;
    }
}

That takes care of resolving the Windows Azure drive letter. Almost. We just need to implement the ResolveCloudDriveLetter-class.

lass ResolveCloudDriveLetter
{
    public static string Resolve(string physicalPath, string vhdFileName)
    {
        // Get the Azure drives currently mounted
        IDictionary<string, Uri> mountedDrives = CloudDrive.GetMountedDrives();

        // Query that gets the drive letter using the VHD filename
        var driveLetterQuery = from d in mountedDrives
                               where Path.GetFileName(d.Value.LocalPath) == vhdFileName
                               select d.Key;

        string driveLetter = driveLetterQuery.FirstOrDefault<string>();

        // Return the new path with the correct Azure drive letter
        if (!String.IsNullOrEmpty(driveLetter))
            return physicalPath.Replace("[CloudDynamicDriveLetter]\\", driveLetter);
        else
            return physicalPath;
    }
}

Thoughts

1132907_52864110

My initial idea – and this is the route I hope EPiServer goes with – was to wrap the cloud storage API in a new virtual path provider. Using the cloud drives is more of a workaround and I feel like a mad scientist for implementing it.

However the idea of using a cloud drive to house the application files is, to me, a clever solution. It would mean that the application folders traditionally stored in Program Files could live in a EPiServerApplicationFiles.vhd-file. We wouldn’t need to ship the entire lot each time we publish our website to the cloud.

Also; having done a heavy amount of reflection on the EPiServer virtual path providers I have an even greater respect for the back-end team over at EPiServer. Amazing work!

That’s my two cents so far! Thanks for reading!

blog comments powered by Disqus