Merging anonymous objects into a single anonymous object

In some cases I have a few combinations of route values that I need to pass along to create the URL for a specific route. Rather than using confusing stacked if-cases, e.g:

(x != 0 && y != 0 ? (object)new { xId = 1, yId = 1 } : (x != 0 ? (object)new { xId = 1 } : (y != 0 ? (object)new { yId = 1 } : null)))

That is the 4 possible combinations (both, one or the other, none) for 2 values.

(in case you wonder why I need the if-cases, this is for a template with many pages/links but depending on how it's used the routes can be set up slightly differently and rather than having to find and change every link I want to be able to apply the route values differently based on how the routes are set up, if I don't do that it will add useless querystrings for values that do not map to a route value)

To make this much easier to read it would be great to have a method to merge the anonymous types, e.g.:

Merge((x != 0 ? new { xId = 1 } : null), (y != 0 ? new { yId = 1 } : null))

I found this solution http://stackoverflow.com/a/6802728 but I have a problem which I think comes down to my understanding of what I'm dealing with here.

Here's my variant of this method:

public static dynamic Merge(params object[] objects)
{
    dynamic expando = new ExpandoObject();
    var merged = expando as IDictionary<string, object>;

    foreach (object obj in objects)
        if (obj != null)
            foreach (PropertyInfo property in obj.GetType().GetProperties())
                merged[property.Name] = property.GetValue(obj);

    return merged;
}

When I pass the output of this method (the route values) along with a route name to a HtmlHelper expecting it to create a html link with the specified URL it fails to do so because it does not find a matching route. It seems it cannot access the route values in the merged object. The method return type is dynamic and casting to object or RouteValueDictionary makes no difference.

In what way is the output object different from a normal anonymous object? Is there a way to make it able to access the keys/values inside the merged object?

Answers


In ASP.NET MVC, the HTML helpers like Url.Action or Html.ActionLink also have overloads, which take a RouteValueDictionary. For example:

@{ var x = 1; var y = 1; }

@Html.ActionLink(
    "Go to contacts", "Contact", "Home",
    routeValues: 
        Merge(x != 0 ? new { xId = 1 } : null, y != 0 ? new { yId = 1 } : null),
    htmlAttributes: null))

Using this overload, you can implement the Merge method this way:

public static RouteValueDictionary Merge(params object[] routeValuesObjects)
{
    var result = new RouteValueDictionary();

    foreach (var routeValues in routeValuesObjects)
        foreach (var item in new RouteValueDictionary(routeValues))
            result[item.Key] = item.Value;

    return result;
}

Anonymous object (actually, a compile-time syntactic sugar which ends in a regular class with a random name assigned by the compiler) is different than an ExpandoObject, an implementation of DynamicObject.

A dynamic object doesn't really expose properties but those are resolved at run-time.

I guess ASP.NET MVC inspects those anonymous objects using reflection and properties from an ExpandoObject aren't accessible using regular reflection. That is, routing should implement specific code to handle route parameters using dynamic objects to handle your use case.

Suggestion...

Sadly I believe you'll need to stay in your current approach. Sometimes we want a magic solution, but if this works well in your scenario, I would avoid overcomplicating the code.


Need Your Help

Test individual classes in a Flex Project

flex actionscript-3

I need to write and test xmlStuff.as. I would like to test it on its own without launching the entire application (launched with index.mxml).

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.