Strongly typed AND arbitrary properties in Web API request object

asp.net-web-api, c#

Solution

For `application/json` content type, you can use `DynamicObject` with WebAPI's default JSON formatter.

public class MyNiceRequest : DynamicObject
{
    private Dictionary<string, string> _dynamicMembers = new Dictionary<string, string>();

    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    public string SomeOptionalField { get; set; }

    [JsonIgnore]
    public Dictionary<string, string> DynamicMembers
    {
        get { return _dynamicMembers; }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object value)
    {
        string stringValue;
        var isFound = _dynamicMembers.TryGetValue(binder.Name, out stringValue);
        value = stringValue;
        return isFound;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (value is string)
        {
            _dynamicMembers[binder.Name] = (string)value;
            return true;
        }
        return false;
    }
}

Edit

If you want the object to be serialized with the same format, implement `IDictionary<string, string>`. This is easy, just delegate the interface implementation to `_dynamicMembers`

This solution doesn't work with the default XML and x-www-form-urlencoded formatters :(

Problem

I have an object that looks pretty much like this: ``` public class MyNiceRequest { [Required] public string FirstName { get; set; } [Required] public string LastName { get; set; } public string SomeOptionalField { get; set; } } ``` And my controller looks like the following, pretty standard still: ``` public MyNiceResponse Post(MyNiceRequest request) { ... } ``` From the front end of the calling application, I want to include more fields than the three specified in the object. These fields are runtime generated (controlled via an admin interface), so I cannot apply them to my request class. However, I've not found a good way to retrieve them in the controller. I can make my request object (`MyNiceRequest`) inherit from `Dictionary<string,string>` - then I'll get them all, but they won't be bound to their respective properties on the strongly typed class (seems like the Dictionary is bound before the rest in whatever model binder is used). Plus, more importantly, validation - which is crucial to the application - stops working. I have seen this question, but it doesn't give me anything as the `Request.Content.Read...`-methods give me empty results (since it's already read and bound to a model?). Let's say I want the following fields from the front end: - `FirstName` (should bind to strongly typed, nowhere else) - `LastName` (should bind to strongly typed, nowhere else) - `SomeOptionalField` (should bind to strongly typed, nowhere else) - `RuntimeGenerated1` (should end up in dictionary) - `RuntimeGenerated2` (should end up in dictionary) I want one of two solutions: - Either be able to inherit from `Dictionary<string,string>`, but let the dictionary be bound AFTER the strongly typed properties to let the validation do it's work - Have a separate property on `MyNiceRequest` that could be something like `Dictionary<string,string> TheRest { get; set; }` and bind that to the remaining incoming properties somewhere. Rewriting the front end to pass in the runtime generated fields as a separate collection is not an option. ..and can this at all be achieved by reusing/reordering existing stuff, or will I have to write a complete media type formatter and/or model binder from scratch?

Original source

Related problems