Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Azure, PHP, OpenXML, VSTS, ...

About the author

Maarten Balliauw is currently employed as .NET Technical Consultant at RealDolmen. 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 Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn
View Maarten Balliauw's MVP profile

Search

Latest Twitter

    Follow me on Twitter...

    My projects

    Disclaimer

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

    © Copyright Maarten Balliauw 2010

    MvcSiteMapProvider 2.1.0 released!

    MvcSiteMapProvider The release for MvcSiteMapProvider 2.1.0 has just been posted on CodePlex. MvcSiteMapProvider is, as the name implies, an ASP.NET MVC SiteMapProvider implementation for the ASP.NET MVC framework. Targeted at ASP.NET MVC 2, it provides sitemap XML functionality and interoperability with the classic ASP.NET sitemap controls, like the SiteMapPath control for rendering breadcrumbs and the Menu control.

    Next to a brand new logo, the component has been patched up with several bugfixes, the visibility attribute is back (in a slightly cooler reincarnation) and a number of new extension points have been introduced. Let’s give you a quick overview…

    kick it on DotNetKicks.com

    Extension points

    MvcSiteMapProvider is built wih extensibility in mind. All extension point contracts are defined in the MvcSiteMapProvider.Extensibility namespace. The sample application on the downloads page contains several custom implementations of these extension points.

    Global extension points (valid for the entire provider and all nodes)

    These extension points can be defined when Registering the provider.

      Node key generator

    Keys for sitemap nodes are usually automatically generated by the MvcSiteMapProvider core. If, for reasons of accessing sitemap nodes from code, the generated keys should follow other naming rules, a custom MvcSiteMapProvider.Extensibility.INodeKeyGenerator implementation can be written.

      Controller type resolver

    In order to resolve a controller type and action method related to a specific sitemap node, a MvcSiteMapProvider.Extensibility.IControllerTypeResolver is used. This should normally not be extended, however if you want to make use of other systems for resolving controller types and action methods, this is the logical extension point.

      Action method parameter resolver

    Action method parameters are resolved by using ASP.NET MVC's ActionDescriptor class. If you want to use a custom system for this, a MvcSiteMapProvider.Extensibility.IActionMethodParameterResolver implementation can be specified.

      ACL module

    To determine whether a sitemap node is accessible to a specific user, a MvcSiteMapProvider.Extensibility.IAclModule implementation is used. MvcSiteMapProvider uses two of these modules by default: access is granted or denied by checking for [Authorize] attributes on action methods, followed by the roles attribute that can be specified in the sitemap XML.

      URL resolver

    URLs are generated by leveraging a MvcSiteMapProvider.Extensibility.ISiteMapNodeUrlResolver implementation. If, for example, you want all URLs generated by MvcSiteMapProvider to be in lowercase text, a custom implementation can be created.

      Visibility provider

    In some situations, nodes should be visible in the breadcrumb trail but not in a complete sitemap. This can be solved using the MvcSiteMapProvider.Extensibility.ISiteMapNodeVisibilityProvider extension point that can be specified globally for every node in the sitemap or granularly on a specific sitemap node. A sample is available on the Advanced node visibility page.

    Node-specific extenion points (valid for a single node)

    These extension points can be defined when Creating a first sitemap.

      Dynamic node provider

    In many web applications, sitemap nodes are directly related to content in a persistent store like a database.For example, in an e-commerce application, a list of product details pages in the sitemap maps directly to the list of products in the database. Using dynamic sitemaps, a small class implementing MvcSiteMapProvider.Extensibility.IDynamicNodeProvider or extending MvcSiteMapProvider.Extensibility.DynamicNodeProviderBase can be provided to the MvcSiteMapProvider offering a list of dynamic nodes that should be incldued in the sitemap. This ensures the product pages do not have to be specified by hand in the sitemap XML.

    A sample can be found on the Dynamic sitemaps page.

      URL resolver

    URLs are generated by leveraging a MvcSiteMapProvider.Extensibility.ISiteMapNodeUrlResolver implementation. If, for example, you want the URL for a sitemap node generated by MvcSiteMapProvider to be in lowercase text, a custom implementation can be created.

      Visibility provider

    In some situations, nodes should be visible in the breadcrumb trail but not in a complete sitemap. This can be solved using the MvcSiteMapProvider.Extensibility.ISiteMapNodeVisibilityProvider extension point that can be specified globally for every node in the sitemap or granularly on a specific sitemap node. A sample is available on the Advanced node visibility page.

    Conclusion

    Only one conclusion: grab the latest bits and start playing with them! And feel free to bug me with feature requests and issues found.

    Also, follow me on Twitter for updates on this project.


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

    ASP.NET MVC - MvcSiteMapProvider 2.0 is out!

    I’m very proud to announce the release of the ASP.NET MVC MvcSiteMapProvider 2.0! I’m also proud that the name of this product now exceeds the average length of Microsoft product names. In this blog post, I will give you a feel of what you can (and can not) do with this ASP.NET-specific SiteMapProvider.

    As a warning: if you’ve used version 1 of this library, you will notice that I have not thought of backwards compatibility. A lot of principles have also changed. For good reasons though: this release is a rewrite of the original version with improved features, extensibility and stability.

    The example code is all based on the excellent ASP.NET MVC Music Store sample application by Jon Galloway.

    Getting the bits

    As always, the bits are available on CodePlex: MvcSiteMapProvider 2.0.0
    If you prefer to have the full source code, download the example application or check the source code tab on CodePlex.

    Introduction

    MvcSiteMapProvider is, as the name implies, an ASP.NET MVC SiteMapProvider implementation for the ASP.NET MVC framework. Targeted at ASP.NET MVC 2, it provides sitemap XML functionality and interoperability with the classic ASP.NET sitemap controls, like the SiteMapPath control for rendering breadcrumbs and the Menu control.

    Based on areas, controller and action method names rather than hardcoded URL references, sitemap nodes are completely dynamic based on the routing engine used in an application. The dynamic character of ASP.NET MVC is followed in the MvcSiteMapProvider: there are numerous extensibility points that allow you to extend the basic functionality offered.

    Registering the provider

    After downloading the MvcSiteMapProvider, you will have to add a reference to the assembly in your project. Also, you will have to register the provider in your Web.config file. Add the following code somewhere in the <system.web> section:

    <siteMap defaultProvider="MvcSiteMapProvider" enabled="true">
      <providers>
        <clear />
        <add name="MvcSiteMapProvider"
             type="MvcSiteMapProvider.DefaultSiteMapProvider, MvcSiteMapProvider"
             siteMapFile="~/Mvc.Sitemap"
             securityTrimmingEnabled="true"
             enableLocalization="true"
             scanAssembliesForSiteMapNodes="true"
             skipAssemblyScanOn=""
             attributesToIgnore="bling"
             nodeKeyGenerator="MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider"
             controllerTypeResolver="MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider"
             actionMethodParameterResolver="MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider"
             aclModule="MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider"
             />
      </providers>
    </siteMap>

    The following configuration directives can be specified:

    Directive Required? Default Description
    siteMapFile No ~/Web.sitemap The sitemap XML file to use.
    securityTrimmingEnabled No false Use security trimming? When enabled, nodes that the user can not access will not be displayed in any sitemap control.
    enableLocalization No false Enables localization of sitemap nodes.
    scanAssembliesForSiteMapNodes No false Scan assemblies for sitemap nodes defined in code?
    skipAssemblyScanOn No (empty) Comma-separated list of assemblies that should be skipped when scanAssembliesForSiteMapNodes is enabled.
    attributesToIgnore No (empty) Comma-separated list of attributes defined on a sitemap node that should be ignored by the MvcSiteMapProvider.
    nodeKeyGenerator No MvcSiteMapProvider.DefaultNodeKeyGenerator, MvcSiteMapProvider Class that will be used to generate sitemap node keys.
    controllerTypeResolver No MvcSiteMapProvider.DefaultControllerTypeResolver, MvcSiteMapProvider Class that will be used to resolve the controller for a specific sitemap node.
    actionMethodParameterResolver No MvcSiteMapProvider.DefaultActionMethodParameterResolver, MvcSiteMapProvider Class that will be used to determine the list of parameters on a sitemap node.
    aclModule No MvcSiteMapProvider.DefaultAclModule, MvcSiteMapProvider Class that will be used to verify security and access rules for sitemap nodes.

     

    Creating a first sitemap

    The following is a simple sitemap XML file that can be used with the MvcSiteMapProvider:

    <?xml version="1.0" encoding="utf-8" ?>
    <mvcSiteMap xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-2.0" enableLocalization="true">
      <mvcSiteMapNode title="Home" controller="Home" action="Index" changeFrequency="Always" updatePriority="Normal">
        <mvcSiteMapNode title="Browse Store" controller="Store" action="Index" />
        <mvcSiteMapNode title="Checkout" controller="Checkout" />
      </mvcSiteMapNode>
    </mvcSiteMap>

    The following attributes can be given on an XML node element:

    Attribute Required? Default Description
    title Yes (empty) The title of the node.
    description No (empty) Description of the node.
    area No (empty) The MVC area for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
    controller Yes (empty) The MVC controller for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
    action Yes (empty) The MVC action method for the sitemap node. If not specified, it will be inherited from a node higher in the hierarchy.
    key No (autogenerated) The unique identifier for the node.
    url No (autogenerated based on routes) The URL represented by the node.
    roles No (empty) Comma-separated list of roles allowed to access the node and its child nodes.
    resourceKey No (empty) Optional resource key.
    clickable No True Is the node clickable or just a grouping node?
    targetFrame No (empty) Optional target frame for the node link.
    imageUrl No (empty) Optional image to be shown by supported HtmlHelpers.
    lastModifiedDate No (empty) Last modified date for the node.
    changeFrequency No Undefined Change frequency for the node.
    updatePriority No Undefined Update priority for the node.
    dynamicNodeProvider No (empty) A class name implementing MvcSiteMapProvider.Extensibility.IDynamicNodeProvider and providing dynamic nodes for the site map.

     

    Defining sitemap nodes in code

    In some cases, defining a sitemap node in code is more convenient than defining it in a sitemap xml file. To do this, decorate an action method with the MvcSiteMapNodeAttribute attribute. For example:

    // GET: /Checkout/Complete
    [MvcSiteMapNodeAttribute(Title = "Checkout complete", ParentKey = "Checkout")]
    public ActionResult Complete(int id)
    {
        // ...
    }

    Note that the ParentKey property should be specified to ensure the MvcSiteMapProvider  can determine the hierarchy for all nodes.

    Dynamic sitemaps

    In many web applications, sitemap nodes are directly related to content in a persistent store like a database.For example, in an e-commerce application, a list of product details pages in the sitemap maps directly to the list of products in the database. Using dynamic sitemaps, a small class can be provided to the MvcSiteMapProvider offering a list of dynamic nodes that should be incldued in the sitemap. This ensures the product pages do not have to be specified by hand in the sitemap XML.

    First of all, a sitemap node should be defined in XML. This node will serve as a template and tell the MvcSiteMapProvider infrastructure to use a custom dynamic node procider:

    <mvcSiteMapNode title="Details" action="Details" dynamicNodeProvider="MvcMusicStore.Code.StoreDetailsDynamicNodeProvider, MvcMusicStore" />

    Next, a class implementing MvcSiteMapProvider.Extensibility.IDynamicNodeProvider or extending MvcSiteMapProvider.Extensibility.DynamicNodeProviderBase should be created in your application code. Here’s an example:

    public class StoreDetailsDynamicNodeProvider
        : DynamicNodeProviderBase
    {
        MusicStoreEntities storeDB = new MusicStoreEntities();

        public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
        {
            // Build value
            var returnValue = new List<DynamicNode>();

            // Create a node for each album
            foreach (var album in storeDB.Albums.Include("Genre"))
            {
                DynamicNode node = new DynamicNode();
                node.Title = album.Title;
                node.ParentKey = "Genre_" + album.Genre.Name;
                node.RouteValues.Add("id", album.AlbumId);

                returnValue.Add(node);
            }

            // Return
            return returnValue;
        }
    }

    Cache dependency

    When providing dynamic sitemap nodes to the MvcSiteMapProvider, chances are that the hierarchy of nodes will become stale, for example when adding products in an e-commerce website. This can be solved by specifying a CacheDescriptor on your MvcSiteMapProvider.Extensibility.IDynamicNodeProvider implementation:

    public class StoreDetailsDynamicNodeProvider
        : DynamicNodeProviderBase
    {
        MusicStoreEntities storeDB = new MusicStoreEntities();

        public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
        {
            // ...
        }

        public override CacheDescription GetCacheDescription()
        {
            return new CacheDescription("StoreDetailsDynamicNodeProvider")
            {
                SlidingExpiration = TimeSpan.FromMinutes(1)
            };
        }
    }

    HtmlHelper functions

    The MvcSiteMapProvider provides different HtmlHelper extension methods which you can use to generate SiteMap-specific HTML code on your ASP.NET MVC views. Here's a list of available HtmlHelper extension methods.

    • Html.MvcSiteMap().Menu() - Can be used to generate a menu
    • Html.MvcSiteMap().SubMenu() - Can be used to generate a submenu
    • Html.MvcSiteMap().SiteMap() - Can be used to generate a list of all pages in your sitemap
    • Html.MvcSiteMap().SiteMapPath() - Can be used to generate a so-called "breadcrumb trail"
    • Html.MvcSiteMap().SiteMapTitle() - Can be used to render the current SiteMap node's title

    Note that these should be registered in Web.config, i.e. under <pages> add the following:

    <pages>
        <controls>
            <! -- ... -->
        </controls>
        <namespaces>
            <! -- ... -->
            <add namespace="MvcSiteMapProvider.Web.Html" />
        </namespaces>
    </pages>

    Action Filter Attributes

    SiteMapTitle

    In some situations, you may want to dynamically change the SiteMap.CurrentNode.Title in an action method. This can be done manually by setting SiteMap.CurrentNode.Title, or by adding the SiteMapTitle action filter attribute.

    Imagine you are building a blog and want to use the Blog's Headline property as the site map node title. You can use the following snippet:

    [SiteMapTitle("Headline")]
    public ViewResult Show(int blogId) {
       var blog = _repository.Find(blogIdId);
       return blog;
    }

    You can also use a non-strong typed ViewData value as the site map node title:

    [SiteMapTitle("SomeKey")]
    public ViewResult Show(int blogId) {
       ViewData["SomeKey"] = "This will be the title";

       var blog = _repository.Find(blogIdId);
       return blog;
    }

    Exporting the sitemap for search engine indexing

    When building a website, chances are that you want to provide an XML sitemap used for search engine indexing. The XmlSiteMapResult class creates an XML sitemap that can be submitted to Google, Yahoo and other search engines to help them crawl your website better. The usage is very straightforward:

    public class HomeController
    {
        public ActionResult SiteMapXml()
        {
            return new XmlSiteMapResult();
        }
    }

    Optionally, a starting node can also be specified in the constructor of theXmlSiteMapResult .

    Conclusion

    Get it while it’s hot! MvcSiteMapProvider 2.0.0 is available on CodePlex.

    kick it on DotNetKicks.com


    Categories: ASP.NET | C# | General | MVC | Projects | Software | XML

    Running on Windows Azure - ChronoRace - Autoscaling

    image At RealDolmen, we had the luck of doing the first (known) project on Windows Azure in Belgium. Together with Microsoft, we had the opportunity to make the ChronoRace website robust enough to withstand large sports events like the 20km through Brussels.

    ChronoRace is a Belgian company based in Malmédy, specialised in electronic timing for large sports events (around 340 per year) troughout Europe in different disciplines like jogging, cycling, sailing, … Each participant is registered through the website, can consult their results and can view a high-definition video of their arrival at the finish line. Of course, these results and videos are also used to brag to co-workers and family members, resulting in about 10 result and video views per participant. Imagine 20.000 or more participants on large events… No wonder their current 1 webserver – 1 database server setup could not handle all load.

    Extra investments in hardware, WAN connection upgrades and video streaming were considered, however found too expensive to have these running for 365 days a year while on average this extra capacity would only be needed for 14 days a year. Ideal for cloud computing! Especially with an expected 30.000 participants for the 20km through Brussels... (which would mean 3 TB of data transfers for the videos on 1 day!!!)

    Microsoft selected RealDolmen as a partner for this project, mainly because of the knowledge we built over the past year, since the first Azure CTP. Together with ChronoRace, we gradually moved the existing SQL Server databse to SQL Azure. After that, we started moving the video streaming to blob storage, implemented extra caching and automatic scaling.

    You probably have seen the following slides in various presentations on cloud computing:

    Capacity cloud computing

    All marketing gibberish, right? Nope! We actually managed to get very close to this model using our custom autoscaling mechanism. Here are some figures we collected during the peak of the 20km through Brussels:

    Windows Azure Auto Scaling

    More information on the technical challenges we encountered can be found in my slide deck I presented at KAHOSL last week:

    If you want more information on scalability and automatic scaling, feel free to come to the Belgian Community Day 2010 where I will be presenting a session on this topic.

    Oh and for the record: I’m not planning on writing marketing posts. I just was so impressed by our actual autoscaling graph that I had to blog this :-)


    Extension methods for PHP

    PHP Extension Methods The concept of “extension” methods will be nothing new to this blog’s .NET-related audience. For the PHP-related audience, this is probably something new. Let’s start with the official definition for extension methods: Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type.

    Let’s visualize this. Imagine having the following class:

    class HelloWorld
    {
        public function sayHello($to = 'World', $from = null)
        {
            $helloString = "Hello, " . $to . "!";
            if (!is_null($from)) {
                $helloString .= " From " . $from;
            }

            echo $helloString . "\r\n";
        }
    }

    Not too difficult. There’s a sayHello() method that allows you to optionally pass who should be greeted and who the greeting is from. Imagine you would like to have a sayHelloTo() method as well, a shortcut to sayHello($to). And some other helper methods that use sayHello() under the hood. Wouldn’t it be nice to have the HelloWorld class concentrating on the real logic and defining the helper/utility functions somewhere else? How about a class named HelloExtensions:

    class HelloWorldExtensions
    {
        public static function sayHelloTo(HelloWorld $subject, $name = '')
        {
            $subject->sayHello($name);
        }

        public static function sayHelloFrom(HelloWorld $subject, $name = '')
        {
            $subject->sayHello('World', $name);
        }
    }

    Ok, nice. But this means I should call HelloWorldExtensions::sayHelloTo(‘Maarten’); to use the utility functions, right? Well: no! Not if we implement the concept of extension methods. Let’s see what we can do to HelloWorld in order to make our life easier.

    class HelloWorld
    {
        public function sayHello($to = 'World', $from = null)
        {
            $helloString = "Hello, " . $to . "!";
            if (!is_null($from)) {
                $helloString .= " From " . $from;
            }

            echo $helloString . "\r\n";
        }

        public function __call($functionName, $arguments = array())
        {
            // Add an extra parameter
            array_unshift($arguments, $this);

            // Current reflected class
            $reflectedClass = new reflectionObject($this);

            // Find suitable class and function
            $availableClasses = get_declared_classes();
            foreach ($availableClasses as $class) {
                $classDefinition = new ReflectionClass($class);
                $availableMethods = $classDefinition->getMethods();
                foreach ($availableMethods as $method) {
                    if ($method->isStatic() && $method->getName() == $functionName) {
                        $availableParameters = $method->getParameters();
                        if ($availableParameters[0]->getClass()->getName() == $reflectedClass->getName()) {
                            $method->invokeArgs(null, $arguments);
                        }
                    }
                }
            }
        }
    }

    The magic method __call() is used to look for a class that defines a static method with the first parameter of our type, HelloWorld. This now allows us to write very clean calls to code, as well as simplify our HelloWorld class:

    $helloWorld = new HelloWorld();
    $helloWorld->sayHello();
    $helloWorld->sayHelloTo('Maarten');
    $helloWorld->sayHelloFrom('Maarten');

    Sweet, no? Can someone get this into the PHP core? I would greatly appreciate it: my classes can focus on functionality and all utility functions that just pass around variables can be defined in a separate class.


    Using Windows Azure Drive in PHP (or Ruby)

    At the JumpIn Camp in Zürich this week, we are trying to get some of the more popular PHP applications running on Windows Azure. As you may know, Windows Azure has different storage options like blobs, tables, queues and drives. There’s the Windows Azure SDK for PHP for most of this, except for drives. Which is normal: drives are at the operating system level and have nothing to do with the REST calls that are used for the other storage types. By the way: I did a post on using Windows Azure Drive (or “XDrive”) a while ago if you want more info.

    Unfortunately, .NET code is currently the only way to create and mount these virtual hard drives from Windows Azure. But luckily, IIS7 has this integrated pipeline model which Windows Azure is also using. Among other things, this means that services provided by managed modules (written in .NET) can now be applied to all requests to the server, not just ones handled by ASP.NET! In even other words: you can have some .NET code running in the same request pipeline as the FastCGI process running PHP (or Ruby). Which made me think: it should be possible to create and mount a Windows Azure Drive in a .NET HTTP module and pass the drive letter of this thing to PHP through a server variable. And here’s how...

    Note: I’ll start with the implementation part first, the usage part comes after that. If you don’t care about the implementation, scroll down...

    Download source code and binaries at http://phpazurecontrib.codeplex.com.

    kick it on DotNetKicks.com

    Building the Windows Azure Drive HTTP module

    Building HTTP modules in .NET is easy! Simply reference the System.Web assembly and create a class implementing IHttpModule:

    public class AzureDriveModule : IHttpModule
    {
        void IHttpModule.Dispose()
        {
            throw new NotImplementedException();
        }

        void IHttpModule.Init(HttpApplication context)
        {
            throw new NotImplementedException();
        }
    }

    There’s our skeleton! Now for the implementation… (Note: insane amount of code coming!)

    public class AzureDriveModule : IHttpModule
    {
        #region IHttpModule Members

        public void Init(HttpApplication context)
        {
            // Initialize config environment
            CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
            {
                configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
            });

            // Initialize local cache
            CloudDrive.InitializeCache(
                RoleEnvironment.GetLocalResource("cloudDriveCache").RootPath,
                RoleEnvironment.GetLocalResource("cloudDriveCache").MaximumSizeInMegabytes);

            // Determine drives to map
            for (int i = 0; i < 10; i++)
            {
                string driveConnectionString = null;
                try
                {
                    driveConnectionString = RoleEnvironment.GetConfigurationSettingValue("CloudDrive" + i);
                }
                catch (RoleEnvironmentException) { }

                if (string.IsNullOrEmpty(driveConnectionString))
                {
                    continue;
                }

                string[] driveConnection = driveConnectionString.Split(new char[] { ';' });

                // Create storage account
                CloudStorageAccount storageAccount = CloudStorageAccount.FromConfigurationSetting(driveConnection[0]);
                CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

                // Mount requested drive
                blobClient.GetContainerReference(driveConnection[1]).CreateIfNotExist();

                var drive = storageAccount.CreateCloudDrive(
                    blobClient
                        .GetContainerReference(driveConnection[1])
                        .GetPageBlobReference(driveConnection[2])
                        .Uri.ToString()
                );

                try
                {
                    drive.Create(int.Parse(driveConnection[3]));
                }
                catch (CloudDriveException ex)
                {
                    // handle exception here
                    // exception is also thrown if all is well but the drive already exists
                }

                string driveLetter = drive.Mount(
                    RoleEnvironment.GetLocalResource("cloudDriveCache").MaximumSizeInMegabytes, DriveMountOptions.None);

                // Add the drive letter to the environment
                Environment.SetEnvironmentVariable("CloudDrive" + i, driveLetter);
            }
        }

        public void Dispose()
        {
        }

        #endregion
    }

    Configuring and using Windows Azure Drive

    There are 4 steps involved in using the Windows Azure Drive HTTP module:

    1. Copy the .NET assemblies into your project
    2. Edit ServiceConfiguration.cscfg (and ServiceDefinition.csdef)
    3. Edit Web.config
    4. Use the thing!

    The Windows Azure tooling for Eclipse will be used in the following example.

    Copy the .NET assemblies into your project

    Create a /bin folder in your web role project and copy in all .DLL files provided. Here’s a screenshot of how this looks:

    .NET assemblies for XDrive in PHP on Azure

    Edit ServiceConfiguration.cscfg (and ServiceDefinition.csdef)

    In order to be able to mount, some modifications to ServiceConfiguration.cscfg (and ServiceDefinition.csdef) are required. The ServiceDefinition.csdef file should contain the following additional entries:

    <?xml version="1.0" encoding="utf-8"?>
    <ServiceDefinition name="TestCustomModules" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
      <WebRole name="WebRole" enableNativeCodeExecution="true">
        <ConfigurationSettings>
          <Setting name="CloudDriveConnectionString" />  
          <Setting name="CloudDrive0" />   
        </ConfigurationSettings>
        <InputEndpoints>
          <!-- Must use port 80 for http and port 443 for https when running in the cloud -->
          <InputEndpoint name="HttpIn" protocol="http" port="80" />
        </InputEndpoints>
        <LocalStorage name="cloudDriveCache" sizeInMB="128"/>
      </WebRole>
    </ServiceDefinition>

    Things to note are the cloudDriveCache local storage entry, which is needed for caching access to the virtual drive. The configuration settings are defined for use in ServiceConfiguration.csdef:

    <?xml version="1.0"?>
    <ServiceConfiguration serviceName="TestCustomModules" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
      <Role name="WebRole">
        <Instances count="1"/>
        <ConfigurationSettings>
          <Setting name="CloudDriveConnectionString" value="UseDevelopmentStorage=true" />
          <Setting name="CloudDrive0" value="CloudDriveConnectionString;drives;sampledrive.vhd;64" />
        </ConfigurationSettings>
      </Role>
    </ServiceConfiguration>

    The configuration specifies that a cloud drive “CloudDrive0” (up to “CloudDrive9”) should be mounted using the storage account in “CloudDriveConnectionString”, a storage container named “drives” and a virtual hard disk file named “sampledrive.vhd”. Oh, and the drive should be 64 MB in size.

    Edit Web.config

    Before the HTTP module is used by IIS7 or Windows Azure, the following should be added to Web.config:

    <modules>
      <add name="AzureDriveModule" type="PhpAzureExtensions.AzureDriveModule, PhpAzureExtensions"/>
    </modules>

    Here’s my complete Web.config:

    <?xml version="1.0"?>
    <configuration>
      <system.webServer>
        <!-- DO NOT REMOVE: PHP FastCGI Module Handler -->
        <handlers>
          <clear />
          <add name="PHP via FastCGI"
               path="*.php"
               verb="*"
               modules="FastCgiModule"
               scriptProcessor="%RoleRoot%\approot\php\php-cgi.exe"
               resourceType="Unspecified" />
          <add name="StaticFile" path="*" verb="*" modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule" resourceType="Either" requireAccess="Read" />
        </handlers>
        <!-- Example WebRole IIS 7 Configation -->
        <defaultDocument>
          <files>
            <clear />
            <add value="index.php" />
          </files>
        </defaultDocument>

        <modules>
          <add name="AzureDriveModule" type="PhpAzureExtensions.AzureDriveModule, PhpAzureExtensions"/>
        </modules>
      </system.webServer>
    </configuration>

    Use the thing!

    Next thing to do is use your virtual Windows Azure Drive. The HTTP module adds an entry in the $_SERVER variable, named after the CloudDrive0-9 settings defined earlier. The following code example stores a file on a virtual Windows Azure Drive and reads it back afterwards:

    <?php
    file_put_contents($_SERVER['CloudDrive0'] . '\sample.txt', 'Hello World!');

    echo file_get_contents($_SERVER['CloudDrive0'] . '\sample.txt');

    Source code in PHPAzureContrib (CodePlex)

    Since there already is a project for PHP and Azure contributions, I decided to add this module to that project. The binaries and source code can be found on http://phpazurecontrib.codeplex.com.

    Other possible usages

    The approach I demonstrated above may be used for other scenarios as well:

    • Modifying php.ini before PHP runs. The module you can access would run before FastCGI runs, an ideal moment to modigy php.ini settings and such.
    • Using .NET authentication modules in PHP, check this site for an example.
    • Download updates to PHP automatically if a new version is available and deploy it into your application at runtime. Probably needs some performance tuning but this trick may work. The same goes for static content and script updates by the way. Imagine pulling a website dynamically from blob storage and deploy it onto your web role without any hassle…

    In short: endless possibilities!

    kick it on DotNetKicks.com


    Categories: ASP.NET | Azure | C# | General | PHP | Projects

    Using FTP to access Windows Azure Blob Storage

    A while ago, I did a blog post on creating an external facing Azure Worker Role endpoint, listening for incoming TCP connections. After doing that post, I had the idea of building a Windows Azure FTP server that served as a bridge to blob storage. Lack of time, other things to do, you name it: I did not work on that idea. Until now, that is.

    Being a lazy developer, I did not start from scratch: writing an FTP server may be something that has been done before, and yes: “Binging” for “ Csharp FTP server” led me to this article on CodeGuru.com. Luckily, the author of the article had the idea of abstraction in mind: he did not build his software on top of a real file system, no, he did an abstraction. This would mean I would only have to host this thing in a worker role somehow and add some classes working with blobs and not with files. Cool!

    kick it on DotNetKicks.com Shout it

    Demo of the FTP to Blob Storage bridge

    Well, you can try this one yourself actually… But let’s start with a disclaimer: I’m not logging your account details when you log in. Next, I’m not allowing you to transfer more than 10MB of data per day. If you require this, feel free to contact me and I’ll give you more traffic quotas.

    Open up your favourite FTP client (like FileZilla), and open up an FTP connection to ftp.cloudapp.net. Don’t forget to use your Windows Azure storage account name as the username and the storage account key as the password. Connect, and you’ll be greeted in a nice way:

    Windows Azure Blob FileZilla

    The folders you are seeing are your blob storage containers. Feel free to browse your storage account and:

    • Create, remove and rename blob containers.
    • Create, remove and rename folders inside a container. Note that a .placeholder file will be created when doing this.
    • Upload and download blobs.

    Feels like regular FTP, right? There’s more though… Using the Windows Azure storage API, you can also choose if a blob container is private or public. Why not do this using the FTP client? Right-click a blob container,  pick “File permissions…” and here you are: the public read permission is the one that you can use to control access to a blob container.

    Change container permission through FTP

    Show me the code!

    Well… No! I think it’s not stable enough for releasing it to public yet. But what I will do is share some off my struggles I faced while developing on this.

    Struggle #1: Quotas

    As you may have noticed: I’m not allowing data transfers of more than 10 MB per day per storage account. This is not much, but I did not want  to go pay for other people’s traffic that comes trough a demo app. However: every command you send, every action you take, is generating traffic. I had to choose how this would be logged and persisted.

    The strategy used is that all transferred bytes are counted and stored in a cache in the worker role. I created a dedicated thread that monitors this cache, and regularly persists the traffic log in blob storage. There is no fixed interval in which this happens, it just happens. I’m not sure yet that this is the best way to do it, but I feel it is a good mix between intensity of logging and intensity of an expensive write to blob storage.

    Struggle #2: Sleep

    This is not a technical struggle. Since I had fun, I dedicated a lot of time to this thing, mainly in fine-tuning, testing, testing with multiple concurrent clients, … I learnt that System.Net has some cool classes and also learnt that TcpClient that are closed should also be disposed. Otherwise, the socket will not be released and no new connections will be accepted after a while. Anyway: it caused a lack of sleep. The solution to this was drinking more coffee, just at the moment where I actually was drinking less coffee for over a month or two. I will have to go to coffee-rehab again…

    Struggle #3: FTP PASV mode

    I will not assume you know this because I also didn’t know the exact difference… When a client connects to an FTP server, it will have 2 connections with that server. One on the standard FTP TCP port 21 used for sending commands back and forth, and one on another TCP port used for transferring data. This second connection can be an active one or a passive one.

    The main difference between active and passive FTP lies in the direction of the connection: with active FTP, the FTP server opens a connection to a TCP port on the client, while with passive FTP, the client will open a connection to another TCP port on the server. Here’s more details on that:

    The Basics

    FTP is a TCP based service exclusively. There is no UDP component to FTP. FTP is an unusual service in that it utilizes two ports, a 'data' port and a 'command' port (also known as the control port). Traditionally these are port 21 for the command port and port 20 for the data port. The confusion begins however, when we find that depending on the mode, the data port is not always on port 20.

    Active FTP

    In active mode FTP the client connects from a random unprivileged port (N > 1023) to the FTP server's command port, port 21. Then, the client starts listening to port N+1 and sends the FTP command PORT N+1 to the FTP server. The server will then connect back to the client's specified data port from its local data port, which is port 20.

    From the server-side firewall's standpoint, to support active mode FTP the following communication channels need to be opened:

    • FTP server's port 21 from anywhere (Client initiates connection)
    • FTP server's port 21 to ports > 1023 (Server responds to client's control port)
    • FTP server's port 20 to ports > 1023 (Server initiates data connection to client's data port)
    • FTP server's port 20 from ports > 1023 (Client sends ACKs to server's data port)

    When drawn out, the connection appears as follows:

    In step 1, the client's command port contacts the server's command port and sends the command PORT 1027. The server then sends an ACK back to the client's command port in step 2. In step 3 the server initiates a connection on its local data port to the data port the client specified earlier. Finally, the client sends an ACK back as shown in step 4.

    The main problem with active mode FTP actually falls on the client side. The FTP client doesn't make the actual connection to the data port of the server--it simply tells the server what port it is listening on and the server connects back to the specified port on the client. From the client side firewall this appears to be an outside system initiating a connection to an internal client--something that is usually blocked.

    Passive FTP

    In order to resolve the issue of the server initiating the connection to the client a different method for FTP connections was developed. This was known as passive mode, or PASV, after the command used by the client to tell the server it is in passive mode.

    In passive mode FTP the client initiates both connections to the server, solving the problem of firewalls filtering the incoming data port connection to the client from the server. When opening an FTP connection, the client opens two random unprivileged ports locally (N > 1023 and N+1). The first port contacts the server on port 21, but instead of then issuing a PORT command and allowing the server to connect back to its data port, the client will issue the PASV command. The result of this is that the server then opens a random unprivileged port (P > 1023) and sends the PORT P command back to the client. The client then initiates the connection from port N+1 to port P on the server to transfer data.

    From the server-side firewall's standpoint, to support passive mode FTP the following communication channels need to be opened:

    • FTP server's port 21 from anywhere (Client initiates connection)
    • FTP server's port 21 to ports > 1023 (Server responds to client's control port)
    • FTP server's ports > 1023 from anywhere (Client initiates data connection to random port specified by server)
    • FTP server's ports > 1023 to remote ports > 1023 (Server sends ACKs (and data) to client's data port)

    When drawn, a passive mode FTP connection looks like this:

    In step 1, the client contacts the server on the command port and issues the PASV command. The server then replies in step 2 with PORT 2024, telling the client which port it is listening to for the data connection. In step 3 the client then initiates the data connection from its data port to the specified server data port. Finally, the server sends back an ACK in step 4 to the client's data port.

    While passive mode FTP solves many of the problems from the client side, it opens up a whole range of problems on the server side. The biggest issue is the need to allow any remote connection to high numbered ports on the server. Fortunately, many FTP daemons, including the popular WU-FTPD allow the administrator to specify a range of ports which the FTP server will use. See Appendix 1 for more information.

    The second issue involves supporting and troubleshooting clients which do (or do not) support passive mode. As an example, the command line FTP utility provided with Solaris does not support passive mode, necessitating a third-party FTP client, such as ncftp.

    With the massive popularity of the World Wide Web, many people prefer to use their web browser as an FTP client. Most browsers only support passive mode when accessing ftp:// URLs. This can either be good or bad depending on what the servers and firewalls are configured to support.

    (from http://slacksite.com/other/ftp.html)

    Clear enough? Good! In order to support passive FTP, the Windows Azure worker role should be listening on more ports than only port 21. After doing some research, I found that most FTP servers allow specifying the passive FTP port range. Opening a range of over 1000 TCP ports is also something most FTP servers seem to do. Good, I tried this one on Windows Azure, deployed it and… found out that you can only define a maximum of 5 public endpoints per deployment.

    This led me to re-implementing PASV mode, opening a new port on demand from a pool of 4 public endpoints defined. Again, I deployed this one but this failed as well: there was too much of a delay in opening a new TcpListener on the fly.

    Option three seemed to work: I have a TcpListener open on TCP port 20 all the time and try to dispatch incoming connections immediately. There’s also a downside to this: if users send a lot of PASV requests, there will be a lot of unused connections that may cause the application to crash. So I did a trick here as well: close listening connections after a short delay.

    Conclusion

    Feel free to use the service and if you require more than 10 MB traffic a day, feel free to contact me. I can specify traffic quotas per storage account and may increase traffic quotas for yours.

    kick it on DotNetKicks.com Shout it


    Categories: Azure | C# | General | Projects

    Introducing RealDolmenBlogs.com

    RealDolmenBlogs.com Here’s something I would like to share with you. A few months ago, our company (RealDolmen) started a new website, RealDolmenBlogs.com. This site syndicates content from employee blogs, people with lots of experience in their range of topics. These guys have lots of knowledge to share, but sometimes their blog does not have a lot of attention from, well, you. Since we would really love to share employee knowledge, RealDolmenBlogs.com was born.

    The following topics are covered:

    • .NET
    • Application Lifecycle Management
    • Architecture
    • ASP.NET
    • Biztalk
    • PHP
    • Sharepoint
    • Silverlight
    • Visual Studio

    Make sure to subscribe to the syndicated RSS feed and have quality content delivered to your RSS reader.

    The technical side

    Since I do not like to do blog posts on topic that do not have a technical touch, considered that the first few lines of text of this post are pure marketing in a sense, here’s the technical bit.

    RealDolmenBlogs.com is built on Windows Azure and SQL Azure. As a company we believe there is value in cloud computing, in this case we chose for cloud computing due to the fact that the setup costs for the website were very small (pay-per-use) and that we can easily scale-up the website if needed.

    The software behind the site is a customized version of BlogEngine.NET. It has been extended with a syndication feature, pulling content from employee blogs with a little help of the Argotic syndication framework. Running BlogEngine.NET on Windows Azure is not that hard, especially when you are using SQL Azure as well: the only thing to modify is the connection string to your database and you are done. Well… that is if you don’t care about images and attachments. We had to do some modifications to how BlogEngine.NET handles file uploads and made sure everything is now stored safe and sound in Windows Azure blob storage.

    That being said: enjoy the content that my colleagues are sharing, posts are definitely worth a read!


    Translating routes (ASP.NET MVC and Webforms)

    Localized route in ASP.NET MVC - Translated route in ASP.NET MVC For one of the first blog posts of the new year, I thought about doing something cool. And being someone working with ASP.NET MVC, I thought about a cool thing related to that: let’s do something with routes! Since System.Web.Routing is not limited to ASP.NET MVC, this post will also play nice with ASP.NET Webforms. But what’s the cool thing? How about… translating route values?

    Allow me to explain… I’m tired of seeing URLs like http://www.example.com/en/products and http://www.example.com/nl/products. Or something similar, with query parameters like “?culture=en-US”. Or even worse stuff. Wouldn’t it be nice to have http://www.example.com/products mapping to the English version of the site and http://www.exaple.com/producten mapping to the Dutch version? Better to remember when giving away a link to someone, better for SEO as well.

    Of course, we do want both URLs above to map to the ProductsController in our ASP.NET MVC application. We do not want to duplicate logic because of a language change, right? And what’s more: it’s not fun if this would mean having to switch from <%=Html.ActionLink(…)%> to something else because of this.

    Let’s see if we can leverage the routing engine in System.Web.Routing for this…

    Want the sample code? Check LocalizedRouteExample.zip (23.23 kb).

    Mapping a translated route

    First things first: here’s how I see a translated route being mapped in Global.asax.cs:

    routes.MapTranslatedRoute(
        "TranslatedRoute",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = "" },
        new { controller = translationProvider, action = translationProvider },
        true
    );

    Looks pretty much the same as you would normally map a route, right? There’s only one difference: the new { controller = translationProvider, action = translationProvider } line of code. This line of code basically tells the routing engine to use the object translationProvider as a provider which allows to translate a route value. In this case, the same translation provider will handle translating controller names and action names.

    Translation providers

    The translation provider being used can actually be anything, as long as it conforms to the following contract:

    public interface IRouteValueTranslationProvider
    {
        RouteValueTranslation TranslateToRouteValue(string translatedValue, CultureInfo culture);
        RouteValueTranslation TranslateToTranslatedValue(string routeValue, CultureInfo culture);
    }

    This contract provides 2 method definitions: one for mapping a translated value to a route value (like: mapping the Dutch “Thuis” to “Home”). The other method will do the opposite.

    TranslatedRoute

    The “core” of this solution is the TranslatedRoute class. It’s basically an overridden implementation of the System.Web.Routing.Route class, using the IRouteValueTranslationProvider for translating a route. As a bonus, it also tries to set the current thread culture to the CultureInfo detected based on the route being called. Note that this is just a reasonable guess, not the very truth. It will not detect nl-NL versus nl-BE, for example. Here’s the code:

    public class TranslatedRoute : Route
    {
        // ...

        public RouteValueDictionary RouteValueTranslationProviders { get; private set; }

        // ...

        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            RouteData routeData = base.GetRouteData(httpContext);

            // Translate route values
            foreach (KeyValuePair<string, object> pair in this.RouteValueTranslationProviders)
            {
                IRouteValueTranslationProvider translationProvider = pair.Value as IRouteValueTranslationProvider;
                if (translationProvider != null
                    && routeData.Values.ContainsKey(pair.Key))
                {
                    RouteValueTranslation translation = translationProvider.TranslateToRouteValue(
                        routeData.Values[pair.Key].ToString(),
                        CultureInfo.CurrentCulture);

                    routeData.Values[pair.Key] = translation.RouteValue;

                    // Store detected culture
                    if (routeData.DataTokens[DetectedCultureKey] == null)
                    {
                        routeData.DataTokens.Add(DetectedCultureKey, translation.Culture);
                    }

                    // Set detected culture
                    if (this.SetDetectedCulture)
                    {
                        System.Threading.Thread.CurrentThread.CurrentCulture = translation.Culture;
                        System.Threading.Thread.CurrentThread.CurrentUICulture = translation.Culture;
                    }
                }
            }

            return routeData;
        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            RouteValueDictionary translatedValues = values;

            // Translate route values
            foreach (KeyValuePair<string, object> pair in this.RouteValueTranslationProviders)
            {
                IRouteValueTranslationProvider translationProvider = pair.Value as IRouteValueTranslationProvider;
                if (translationProvider != null
                    && translatedValues.ContainsKey(pair.Key))
                {
                    RouteValueTranslation translation =
                        translationProvider.TranslateToTranslatedValue(
                            translatedValues[pair.Key].ToString(), CultureInfo.CurrentCulture);

                    translatedValues[pair.Key] = translation.TranslatedValue;
                }
            }

            return base.GetVirtualPath(requestContext, translatedValues);
        }
    }

    The GetRouteData finds a corresponding route translation if I entered “/Thuis/Over” in the URL. The GetVirtualPath method does the opposite, and will be used for mapping a call to <%=Html.ActionLink(“About”, “About”, “Home”)%> to a route like “/Thuis/Over” if the current thread culture is nl-NL. This is not rocket science, it simply tries to translate every token in the requested path and update the route data with it so the ASP.NET MVC subsystem will know that “Thuis” maps to HomeController.

    Tying everything together

    We already tied the route definition in Global.asax.cs earlier in this blog post, but let’s do it again with a sample DictionaryRouteValueTranslationProvider that will be used for translating routes. This one goes in Global.asax.cs:

    public static void RegisterRoutes(RouteCollection routes)
    {
        CultureInfo cultureEN = CultureInfo.GetCultureInfo("en-US");
        CultureInfo cultureNL = CultureInfo.GetCultureInfo("nl-NL");
        CultureInfo cultureFR = CultureInfo.GetCultureInfo("fr-FR");

        DictionaryRouteValueTranslationProvider translationProvider = new DictionaryRouteValueTranslationProvider(
            new List<RouteValueTranslation> {
                new RouteValueTranslation(cultureEN, "Home", "Home"),
                new RouteValueTranslation(cultureEN, "About", "About"),
                new RouteValueTranslation(cultureNL, "Home", "Thuis"),
                new RouteValueTranslation(cultureNL, "About", "Over"),
                new RouteValueTranslation(cultureFR, "Home", "Demarrer"),
                new RouteValueTranslation(cultureFR, "About", "Infos")
            }
        );

        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapTranslatedRoute(
            "TranslatedRoute",
            "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = "" },
            new { controller = translationProvider, action = translationProvider },
            true
        );

        routes.MapRoute(
            "Default",      // Route name
            "{controller}/{action}/{id}",   // URL with parameters
            new { controller = "Home", action = "Index", id = ""// Parameter defaults
        );

    }

    This is basically it! What I can now do is set the current thread’s culture to, let’s say fr-FR, and all action links generated by ASP.NET MVC will be using French. Easy? Yes! Cool? Yes!

    Localizing ASP.NET MVC routing

    Want the sample code? Check LocalizedRouteExample.zip (23.23 kb).

    kick it on DotNetKicks.com


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

    PHPMEF 0.1.0 released!

    PHP MEF A while ago, I did a conceptual blog post on PHP Managed Extensibility Framework – PHPMEF. Today, I’m proud to announce the first public release of PHPMEF! After PHPExcel, PHPLinq, PHPPowerPoint and the Windows Azure SDK for PHP, PHPMEF is the 5th open-source project I started on interoperability (or conceptual interoperability) between the Microsoft world and the PHP world. Noble price for peace upcoming :-)

    What is this thing?

    PHPMEF is a PHP port of the .NET Managed Extensibility Framework, allowing easy composition and extensibility in an application using the Inversion of Control principle and 2 easy keywords: @export and @import.

    PHPMEF is based on a .NET library, MEF, targeting extensibility of projects. It allows you to declaratively extend your application instead of requiring you to do a lot of plumbing. All this is done with three concepts in mind: export, import and compose. “PHPMEF” uses the same concepts in order to provide this extensibility features.

    Show me an example!

    Ok, I will. But not here. Head over to http://phpmef.codeplex.com and have a look at the principles and features behind PHPMEF.

    Enjoy!


    Categories: C# | General | MEF | PHP | Projects

    PHP Managed Extensibility Framework – PHPMEF

    image While flying sitting in the airplane to the Microsoft Web Developer Summit in Seattle, I was watching some PDC09 sessions on my laptop. During the MEF session, an idea popped up: there is no MEF for PHP! 3500 kilometers after that moment, PHP got its own MEF…

    What is MEF about?

    MEF is a .NET library, targeting extensibility of projects. It allows you to declaratively extend your application instead of requiring you to do a lot of plumbing. All this is done with three concepts in mind: export, import and compose. (Glenn, I stole the previous sentence from your blog). “PHPMEF” uses the same concepts in order to provide this extensibility features.

    Let’s start with a story… Imagine you are building a Calculator. Yes, shoot me, this is not a sexy sample. Remember I wrote this one a plane with snoring people next to me…The Calculator is built of zero or more ICalculationFunction instances. Think command pattern. Here’s how such an interface can look like:

    interface ICalculationFunction
    {
        public function execute($a, $b);
    }

    Nothing special yet. Now let’s implement an instance which does sums:

    class Sum implements ICalculationFunction
    {
        public function execute($a, $b)
        {
            return $a + $b;
        }
    }

    Now how would you go about using this in the following Calculator class:

    class Calculator
    {
        public $CalculationFunctions;
    }

    Yes, you would do plumbing. Either instantiating the Sum object and adding it into the Calculator constructor, or something similar. Imagine you also have a Division object. And other calculation functions. How would you go about building this in a maintainable and extensible way? Easy: use exports…

    Export

    Exports are one of the three fundaments of PHPMEF. Basically, you can specify that you want class X to be “ exported”  for extensibility. Let’s export Sum:

    /**
      * @export ICalculationFunction
      */

    class Sum implements ICalculationFunction
    {
        public function execute($a, $b)
        {
            return $a + $b;
        }
    }

    Sum is exported as Sum by default, but in this case I want PHPMEF to know that it is also exported as ICalculationFunction. Let’s see why this is in the import part…

    Import

    Import is a concept required for PHPMEF to know where to instantiate specific objects. Here’s an example:

    class Calculator
    {
        /**
          * @import ICalculationFunction
          */

        public $SomeFunction;
    }

    In this case, PHPMEF will simply instantiate the first ICalculationFunction instance it can find and assign it to the Calculator::SomeFunction variable. Now think of our first example: we want different calculation functions in our calculator! Here’s how:

    class Calculator
    {
        /**
          *  @import-many ICalculationFunction
          */

        public $CalculationFunctions;
    }

    Easy, no? PHPMEF will ensure that all possible ICalculationFunction instances are added to the Calculator::CalculationFunctions array. Now how is all this being plumbed together? It’s not plumbed! It’s composed!

    Compose

    Composing matches all exports and imports in a specific application path. How? Easy! Use the PartInitializer!

    // Create new Calculator instance
    $calculator = new Calculator();

    // Satisfy dynamic imports
    $partInitializer = new Microsoft_MEF_PartInitializer();
    $partInitializer->satisfyImports($calculator);

    Easy, no? Ask the PartInitializer to satisfy all imports and you are done!

    Advanced usage scenarios

    The above sample was used to demonstrate what PHPMEF is all about. I’m sure you can imagine more complex scenarios. Here are some other possibilities…

    Single instance exports

    By default, PHPMEF instantiates a new object every time an import has to be satisfied. However, imagine you want our Sum class to be re-used. You want PHPMEF to assign the same instance over and over again, no matter where and how much it is being imported. Again, no plumbing. Just add a declarative comment:

    /**
      * @export ICalculationFunction
      * @export-metadata singleinstance
      */

    class Sum implements ICalculationFunction
    {
        public function execute($a, $b)
        {
            return $a + $b;
        }
    }

    Export/import metadata

    Imagine you want to work with interfaces like mentioned above, but want to use a specific implementation that has certain metadata defined. Again: easy and no plumbing!

    My calculator might look like the following:

    class Calculator
    {
        /**
          *  @import-many ICalculationFunction
          */

        public $CalculationFunctions;

        /**
          *  @import ICalculationFunction
          *  @import-metadata CanDoSums
          */

        public $SomethingThatCanDoSums;
    }

    Calculator::SomeThingThatCanDoSums is now constrained: I only want to import something that has the metadata “CanDoSums” attached. Here’s how to create such an export:

    /**
      * @export ICalculationFunction
      * @export-metadata CanDoSums
      */

    class Sum implements ICalculationFunction
    {
        public function execute($a, $b)
        {
            return $a + $b;
        }
    }

    Here’s an answer to a question you may have: yes, multiple metadata definitions are possible and will be used to determine if an export matches an import.

    One small note left: you can also ask the PartInitializer for the metadata defined on a class.

    // Create new Calculator instance
    $calculator = new Calculator();

    // Satisfy dynamic imports
    $partInitializer = new Microsoft_MEF_PartInitializer();
    $partInitializer->satisfyImports($calculator);

    // Get metadata
    $metadata = $partInitializer->getMetadataForClass('Sum');

    Can I get the source?

    No, not yet. For a number of reasons. I first want to make this thing a bit more stable, as well as deciding if all MEF features should be ported. Also, I’m looking for an appropriate name/library to put this in. You may have noticed the Microsoft_* naming, a small hint to the Interop team in incorporating this as another Microsoft library in the PHP world. Yes Vijay, talking to you :-)


    Categories: General | MEF | PHP | Projects | Software