dynamic? I'll never use that ... or then again, maybe it could ...

Posted by adweigert on ASP.net Weblogs See other posts from ASP.net Weblogs or by adweigert
Published on Thu, 27 May 2010 01:04:00 GMT Indexed on 2010/05/27 5:52 UTC
Read the original article Hit count: 334

Filed under:
|

So, I don't know about you, but I was highly skeptical of the dynamic keywork when it was announced. I thought to myself, oh great, just another move towards VB compliance.

Well after seeing it being used in things like DynamicXml (which I use for this example) I then was working with a MVC controller and wanted to move some things like operation timeout of an action to a configuration file. Thinking big picture, it'd be really nice to have configuration for all my controllers like that. Ugh, I don't want to have to create all those ConfigurationElement objects...

So, I started thinking self, use what you know and do something cool ...

Well after a bit of zoning out, self came up with use a dynamic object duh! I was thinking of a config like this ...

<controllers>
  <add type="MyApp.Web.Areas.ComputerManagement.Controllers.MyController, MyApp.Web">
    <detail timeout="00:00:30" />
  </add>
</controllers>

So, I ended up with a couple configuration classes like this ...

blic abstract class DynamicConfigurationElement : ConfigurationElement
{
    protected DynamicConfigurationElement()
    {
        this.DynamicObject = new DynamicConfiguration();
    }

    public DynamicConfiguration DynamicObject
    {
        get;
        private set;
    }

    protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
    {
        this.DynamicObject.Add(name, value);
        return true;
    }

    protected override bool OnDeserializeUnrecognizedElement(string elementName, XmlReader reader)
    {
        this.DynamicObject.Add(elementName, new DynamicXml((XElement)XElement.ReadFrom(reader)));
        return true;
    }
}

public class ControllerConfigurationElement : DynamicConfigurationElement
{
    [ConfigurationProperty("type", Options = ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsKey)]
    public string TypeName
    {
        get { return (string)this["type"]; }
    }

    public Type Type
    {
        get { return Type.GetType(this.TypeName, true); }
    }
}

public class ControllerConfigurationElementCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new ControllerConfigurationElement();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((ControllerConfigurationElement)element).Type;
    }
}

And then had to create the meat of the DynamicConfiguration class which looks like this ...

public class DynamicConfiguration : DynamicObject
{
    private Dictionary<string, object> properties = new Dictionary<string, object>(StringComparer.CurrentCultureIgnoreCase);

    internal void Add<T>(string name, T value)
    {
        this.properties.Add(name, value);
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var propertyName = binder.Name;

        result = null;

        if (this.properties.ContainsKey(propertyName))
        {
            result = this.properties[propertyName];
        }

        return true;
    }
}

So all being said, I made a base controller class like a good little MVC-itizen ...

public abstract class BaseController : Controller
{
    protected BaseController()
        : base()
    {
        var configuration = ManagementConfigurationSection.GetInstance();
            
        var controllerConfiguration = configuration.Controllers.ForType(this.GetType());

        if (controllerConfiguration != null)
        {
            this.Configuration = controllerConfiguration.DynamicObject;
        }
    }

    public dynamic Configuration
    {
        get;
        private set;
    }
}

And used it like this ...

public class MyController : BaseController
{
    static readonly string DefaultDetailTimeout = TimeSpan.MaxValue.ToString();

    public MyController()
    {
        this.DetailTimeout = TimeSpan.Parse(this.Configuration.Detail.Timeout ?? DefaultDetailTimeout);
    }

    public TimeSpan DetailTimeout
    {
        get;
        private set;
    }
}

And there I have an actual use for the dynamic keyword ... never thoguht I'd see the day when I first heard of it as I don't do much COM work ... oh dont' forget this little helper extension methods to find the controller configuration by the controller type.

public static ControllerConfigurationElement ForType<T>(this ControllerConfigurationElementCollection collection)
{
    Contract.Requires(collection != null);
    return ForType(collection, typeof(T));
}

public static ControllerConfigurationElement ForType(this ControllerConfigurationElementCollection collection, Type type)
{
    Contract.Requires(collection != null);
    Contract.Requires(type != null);
    return collection.Cast<ControllerConfigurationElement>().Where(element => element.Type == type).SingleOrDefault();
}

Sure, it isn't perfect and I'm sure I can tweak it over time, but I thought it was a pretty cool way to take advantage of the dynamic keyword functionality. Just remember, it only validates you did it right at runtime, which isn't that bad ... is it? And yes, I did make it case-insensitive so my code didn't have to look like my XML objects, tweak it to your liking if you dare to use this creation.

© ASP.net Weblogs or respective owner

Related posts about c#

Related posts about dynamic