@Shazwazza

Shannon Deminick's blog all about web development

Making a DynamicObject implementation be case insensitive

February 5, 2016 13:25

To me this seems to be a very straight forward request but I cannot find the ‘easy’ way to do this in .Net even though there seems to be a clear indication that this is supported Out of The Box. Here’s an example of what I want to do:

//Simple class
public class Foo
{
    public string Hello {get;set;}
    public string World {get;set;}
    public int AddNumbers(int num1, int num2)
    {
        return num1 + num2;
    }
}

//Use the simple class but allow case insensitive
// access to it's properties and members
var foo = new Foo{ Hello = "hi", World = "earth" };

//make it dynamic
dynamic dFoo = foo;

//access the dynamic object's props and methods 
// in a case insensitive way:
Assert.AreEqual(foo.Hello, dFoo.hello);
Assert.AreEqual(foo.World, dFoo.world);
Assert.AreEqual(foo.AddNumbers(1,2), dFoo.addNumbers(1,2));

DynamicObject

In order to do the above, you can implement ‘DynamicObject’ and override a couple of methods:

public class Foo : DynamicObject
{
    public string Hello {get;set;}
    public string World {get;set;}
    public int AddNumbers(int num1, int num2)
    {
        return num1 + num2;
    }
    
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        //Here we would need to match lower cased binder.Name
        // to a real method that our object has
        var name = binder.Name.ToLowerInvariant();
        //TODO: Do the matching
        
        return base.TryInvokeMember(binder, args, out result);
    }
    
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        //Here we would need to match lower cased binder.Name
        // to a real property that our object has
        var name = binder.Name.ToLowerInvariant();
        //TODO: Do the matching
        
        return base.TryGetMember(binder, out result);
    }
}

I don’t know about you, but I’d rather not have to write all the code to do the matching and executing. This seems like a pretty straight forward thing to want to do and after looking at the innards of some of this stuff, it turns out that the ‘GetMemberBinder’ object has a property conveniently called `IgnoreCase`… surely this functionality already exists!?

Well I unfortunately cannot find anything worthwhile on Google about ‘GetMemberBinder.IgnoreCase’, I also can’t really see where or how it gets used when looking at decompiled sources.

So I’ve reverted to handling this on my own but I would really really really like to know if there’s a built in and simple way to do this in .Net since it sure seems like it should exist.

CaseInsensitiveDynamicObject

This is a base class implementation to achieve what I want. It’s not error proof and I’m sure with some objects it won’t work 100% as expected but for simple objects this will work. Using this implementation is super easy:

public class Foo : CaseInsensitiveDynamicObject
{
    public string Hello {get;set;}
    public string World {get;set;}
    public int AddNumbers(int num1, int num2)
    {
        return num1 + num2;
    }
}

By doing that, my original requirement works! Here’s the code that does this … because I was lazy and didn’t want to create more classes than I needed I’m using Tuples so the code is fully of super generic fun ;)

/// <summary>
/// This will check enable dynamic access to properties and methods in a case insensitive manner
/// </summary>
/// <typeparam name="T"></typeparam>
/// <remarks>
/// This works by using reflection on the type - the reflection lookup is lazy so it will not execute unless a dynamic method needs to be accessed
/// </remarks>
public abstract class CaseInsensitiveDynamicObject<T> : DynamicObject
    where T: class
{
    /// <summary>
    /// Used for dynamic access for case insensitive property access
    /// </summary>`
    private static readonly Lazy<IDictionary<string, Func<T, object>>> CaseInsensitivePropertyAccess = new Lazy<IDictionary<string, Func<T, object>>>(() =>
    {
        var props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .DistinctBy(x => x.Name);
        return props.Select(propInfo =>
        {
            var name = propInfo.Name.ToLowerInvariant();
            Func<T, object> getVal = propInfo.GetValue;
            return new KeyValuePair<string, Func<T, object>>(name, getVal);

        }).ToDictionary(x => x.Key, x => x.Value);
    });

    /// <summary>
    /// Used for dynamic access for case insensitive property access
    /// </summary>
    private static readonly Lazy<IDictionary<string, Tuple<ParameterInfo[], Func<T, object[], object>>>> CaseInsensitiveMethodAccess
        = new Lazy<IDictionary<string, Tuple<ParameterInfo[], Func<T, object[], object>>>>(() =>
        {
            var props = typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public)
                .Where(x => x.IsSpecialName == false && x.IsVirtual == false)
                .DistinctBy(x => x.Name);
            return props.Select(methodInfo =>
            {
                var name = methodInfo.Name.ToLowerInvariant();
                Func<T, object[], object> getVal = methodInfo.Invoke;
                var val = new Tuple<ParameterInfo[], Func<T, object[], object>>(methodInfo.GetParameters(), getVal);
                return new KeyValuePair<string, Tuple<ParameterInfo[], Func<T, object[], object>>>(name, val);

            }).ToDictionary(x => x.Key, x => x.Value);
        });

    /// <summary>
    /// Used for dynamic access for case insensitive method access
    /// </summary>
    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        var name = binder.Name.ToLowerInvariant();
        if (CaseInsensitiveMethodAccess.Value.ContainsKey(name) == false)
            return base.TryInvokeMember(binder, args, out result);

        var val = CaseInsensitiveMethodAccess.Value[name];
        var parameters = val.Item1;
        var callback = val.Item2;
        var fullArgs = new List<object>(args);
        if (args.Length <= parameters.Length)
        {
            //need to fill them up if they're optional
            for (var i = args.Length; i < parameters.Length; i++)
            {
                if (parameters[i].IsOptional)
                {
                    fullArgs.Add(parameters[i].DefaultValue);
                }
            }
            if (fullArgs.Count == parameters.Length)
            {
                result = callback((T)(object)this, fullArgs.ToArray());
                return true;
            }
        }
        return base.TryInvokeMember(binder, args, out result);
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        var name = binder.Name.ToLowerInvariant();
        if (CaseInsensitivePropertyAccess.Value.ContainsKey(name) == false)
            return base.TryGetMember(binder, out result);

        result = CaseInsensitivePropertyAccess.Value[name]((T)(object)this);
        return true;
    }
}

 

If anyone know how to make this work OOTB with .Net, perhaps using the `GetMemberBinder.IgnoreCase` I’d love to hear it!

Razor + dynamic + internal + interface & the 'object' does not contain a definition for 'xxxx' exception

April 11, 2013 16:49

I’ve come across this strange issue and decided to blog about it since I can’t figure out exactly why this is happening it is clearly something to do with the DLR and interfaces.

First, if you are getting the exception: 'object' does not contain a definition for 'xxxx' it is related to either an object you have being marked internal or you are are using an anonymous object type for your model (which .Net will always mark as internal).

Here’s a really easy way to replicate this:

1. Create an internal model

internal class MyModel
{
public string Name {get;set;}
}

2. Return this model in your MVC action

public ActionResult Index()
{
return View(new InternalTestModel("Shannon"));
}

3. Make your view have a dynamic model and then try to render the model’s property

@model dynamic
<h1>@Model.Name</h1>

You’ll get the exception:

Server Error in '/' Application.

'object' does not contain a definition for 'Name'

So even though the error is not very informative, it makes sense since Razor is trying to access an internal class.

Try using a public interface

Ok so if we want to keep our class internal, we could expose it via a public interface. Our code might then look like this:

public interface IModel 
{
string Name {get;}
}
internal class MyModel : IModel
{
public string Name {get;set;}
}

Then we can change our view to be strongly typed like:

@model IModel
<h1>@Model.Name</h1>

And it will work, however if you change your view back to be @model dynamic you will still get the exception. Pretty sure it’s because the DLR is just binding to the instance object and doesn’t really care about the interface… this makes sense.

Try using an abstract public class

For some reason if you make your internal class inherit from a public abstract class that implements the same interface you will not get this exception even though the object instance you are passing to razor is internal. For example with this structure:

public interface IModel 
{
string Name {get;}
}
public abstract class ModelBase : IModel
{
public abstract Name {get;}
}
internal class MyModel : IModel
{
public override string Name {get;set;}
}

You will not get this error if your view is @model dynamic.

Strange!

I’m sure there’s something written in the DLR spec about this but still seems strange to me! If you are getting this error and you aren’t using anonymous objects for your model, hopefully this might help you figure out what is going on.