3

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.

Jay Pete
  • 4,123
  • 4
  • 35
  • 51
  • Have you checked [source files](https://github.com/NancyFx/Nancy/blob/master/src/Nancy/DynamicDictionary.cs)? – Aleksandr Ivanov Apr 05 '15 at 21:30
  • Yes I have... and it seems to do the job. I just wanted some more insight on what it was intended for. If it isn't intended to solve my problem, I don't want to use it. – Jay Pete Apr 05 '15 at 23:18

2 Answers2

7

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.

Phill
  • 18,398
  • 7
  • 62
  • 102
  • Thanks Phil! I figured as much, and played around with all last night. Instead of creating nested dynamics, I am nesting objects as DynamicDictionary so I get nested dictionaries. That is giving my a bit of a head ache, because I have to cast it from dynamic to DynamicDictionary to actually use it as a DynamicDictionary. I'll take a look at the model binding. The code for NancyFx is really well structured and easily read. Very impressed! – Jay Pete Apr 06 '15 at 09:41
1

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;
}
Jay Pete
  • 4,123
  • 4
  • 35
  • 51
  • Because `var` is not dynamic. If you changed your code to be `dynamic person = dictionary.person;` then you can access `person.name` :) `TryGetMember` only works on types defined as `dynamic`, it doesn't do that when you use `var`. – Phill Apr 06 '15 at 11:49
  • Of course... why didn't I see that. But if I then want to make use of the dictionary part, and access the collection of keys instead, then I have to cast it to a DynamicDictionary. – Jay Pete Apr 06 '15 at 21:25
  • Or I can just access .Value on the object, and that will also give me the DynamicDictionary. But I guess an explicit cast better explains what is going on. – Jay Pete Apr 06 '15 at 21:33