Runtime Dynamic Actions in MonoRail with Windsor

Another loosely coupled post on Going to Production with MonoRail.

The ability to customize every aspect of an application Lego style is great, but there will always be client needs that are absolutely off the wall.  Not a customization of an existing component, but something so specific and unique to the client's domain that it cannot be encapsulated into something reusable.  My mind struggled with the paradox, how do I architect the ability to insert non-reusable functionality in a reusable way?  Mike Nichols put me on the path to my solution.

Dynamic Actions are a feature of MonoRail that allow you to decouple an action from a controller.  This sounds pretty simple but when you read through the documentation and the many ways that developers are putting them into practice, Dynamic Actions reveal their true power.  The first part of my solution: completely custom functionality is simply implemented by a class that implements IDynamicAction.  Each piece of custom functionality corresponds to a dynamic action.  The next question is how do I expose this functionality to the Client?  How do I ensure that this functionality is only exposed to the Client that paid for it?

I haven't yet posted about how we manage configuration for each client, but essentially we have an INI file (cringe! gasp! moan!) for each client hosted on a particular server.  A sample section of the INI file for the Contoso client would look like so:

[Actions]
DoWork=ContosoDoWorkAction
DoWork2=ContosoDoWork2Action

The key (DoWork) represents the name of the action, the value (ContosoDoWorkAction) is the name of a class that implements IDynamicAction and the logic for the custom functionality required by the client.  Actions are loaded into the child container for the Client during application startup.  Now we need a controller that we can attach these actions to at runtime.  This is achieved using the code sample below:

namespace My.Application
{
public class CustomController : Controller
{
public CustomController(IKernel kernel)
{
ClientConfiguration clientConfig = ClientConfigurationManager.GetCurrent();

NameValueCollection actions = clientConfig.Actions;

foreach (string actionName in actions.AllKeys)
{
string actionTypeName = actions[actionName];
Type actionType = Utility.GetImplType(actionTypeName);
DynamicActions[actionName] = (IDynamicAction)kernel.Resolve(actionType);
}
}
}
}

So simple it should be criminal.  With the configuration sample above and the CustomController implementation, we now have valid URLs of:

https://contoso.myb2bapplication.com/custom/DoWork.rails

https://contoso.myb2bapplication.com/custom/DoWork2.rails

At the same time, by the nature of our child container configuration magic, the following URLs would be invalid:

https://fabrikam.myb2bapplication.com/custom/DoWork.rails

https://fabrikam.myb2bapplication.com/custom/DoWork2.rails

This is because the actions are only available to the client for which they were configured.  By the same token I can disable an action by commenting out the line in the configuration file.

Who's your daddy paradox?

«June»
SunMonTueWedThuFriSat
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345