NancyFx DynamicDictionary

2.4k views Asked by At

I am trying to understand the DynamicDictionary in NancyFX, it looks pretty cool. Does anyone know of a blog post or similar, that goes through the internals of it?

I need a propertybag to pass around objects, that I don't know the content of because they come from outside my system as JSON. But based on the contents of these objects, such as the presence of certain properties I need to do stuff.

I could just pass around dynamic objects, but that is a bit too vague I think. Don't really like that.

I would need nested dictionaries, to fully represent the object graph.

2

There are 2 answers

1
Phill On BEST ANSWER

The dynamic dictionary is just a ExpandoObject with a Dictionary in it. So it can still be accessed like a dictionary.

For example, in MVC you access Form properties like so:

var name = Request["name"];

or

var name = Request.Form["name"];

When a request comes into Nancy you can access it via the dot notation. Or via the class indexer.

var name = parameters.name;
var name = parameters["name"];

This is handy when you're sending query string or form names that have values that cannot be used in dot notation.

var firstName = parameters["first-name"];

The values are also dynamic, so it could be made up of nested objects. This allows you to do stuff like:

var firstName = parameters.contact.firstname;

So if you're passing a JSON payload to the request then you can access the entire structure using dot notation.

However you will probably find most developers using Nancy only ever access Route values or QueryString values using this method.

Get["/products/{id:int}/"] = parameters => {
   int id = parameters.id;
};

So back to the original question:

Is there a blog post or any doco: Nope.

Why does it exist: For sugar syntax.

Can I use it for what I want: Yes absolutely!

Can you tell me how to use it: Nope, however it shouldn't be hard. Just look the model binding in Nancy to figure it out. It's not too hard.


Just an edit based on the answer by the OP.

When you access the dot notation, continued dot notation will only work on further dynamic types.

This means using var will cause an exception because of the way var and dynamic are handled by the compiler.

When you do:

var person = parameters.person;
var name = person.name;

parameters is currently dynamic and implements TryGetMember, this internally looks up a dictionary of values and attempts to return the value.

When you define the object as var for the person variable. The compiler assumes that anything after that exists on the object, so it looks for name on the person variable.

Since name does not exist as a member of person it will throw.

To resolve this, the variable must be assigned as dynamic. So the example becomes:

dynamic person = parameters.person;
var name = person.name;

This will work.

3
Jay Pete On

So I started working with the DynamicDictionary and it is pretty cool and easy to work with. Only one thing bugs me right now. That is if I nest DynamicDictionaries.

Look at the following example:

private void TestNestedDynamicDictionary()
{
    dynamic dictionary = new DynamicDictionary();
    dynamic nestedDictionary = new DynamicDictionary();
    nestedDictionary.Add("name", "Peter");
    dictionary.Add("person", nestedDictionary);

    var person = dictionary.person;
    var name = person.name;

    Console.WriteLine(name);
}

This fails when trying to access person.name with a 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:

DynamicDictionaryValue' does not contain a definition for 'name'

If I just do an explicit cast like this it works.

    var person = (DynamicDictionary)dictionary.person;

Any input on how I could make it behave as DynamicDictionary right out of the box... apart from checking the DynamicDictionaryValue before it is returned, and do the cast there, which I think is messy.

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
    object value;
    if (!dictionary.TryGetValue(binder.Name, out value))
    {
        result = new DynamicDictionaryValue(null);
        return true;
    }

    var dictVal = value as DynamicDictionaryValue;
    if (null != dictVal && dictVal.Value is DynamicDictionary)
    {
        result = dictVal.Value;
    }
    else
    {
        result = value;
    }

    return true;
}