Question

I'm trying to migrate to ASP.Net MVC 2 and meet some issues. Here is one : I needed to bind directly a Dictionary as result of a view post.

In ASP.Net MVC 1 it worked perfectly using a custom IModelBinder :

/// <summary>
/// Bind Dictionary<int, int>
/// 
/// convention : <elm name="modelName_key" value="value"></elm>
/// </summary>
public class DictionaryModelBinder : IModelBinder
{
    #region IModelBinder Members

    /// <summary>
    /// Mandatory
    /// </summary>
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        IDictionary<int, int> retour = new Dictionary<int, int>();

        // get the values
        var values = bindingContext.ValueProvider;
        // get the model name
        string modelname = bindingContext.ModelName + '_';
        int skip = modelname.Length;

        // loop on the keys
        foreach(string keyStr in values.Keys)
        {
            // if an element has been identified
            if(keyStr.StartsWith(modelname))
            {
                // get that key
                int key;
                if(Int32.TryParse(keyStr.Substring(skip), out key))
                {
                    int value;
                    if(Int32.TryParse(values[keyStr].AttemptedValue, out value))
                        retour.Add(key, value);
                }
            }
        }
        return retour;
    }

    #endregion
}

It worked in pair with some smart HtmlBuilder that displayed dictionary of data.

The problem I meet now is that ValueProvider is not a Dictionary<> anymore, it's a IValueProvider that only allow to get values whose name is known

public interface IValueProvider
{
    bool ContainsPrefix(string prefix);
    ValueProviderResult GetValue(string key);
}

This is really not cool as I cannot perform my smart parsing...

Question :

  1. Is there another way to get all keys ?
  2. Do you know another way to bind a collection of HTML elements to a Dictionary

Thanks for your suggestions

O.

Was it helpful?

Solution

I don't think you'll be able to do it this way anymore in MVC 2.
Alternatively, you could extend DefaultModelBinder and override one of its virtual methods like GetModelProperties and then change the ModelName inside the ModelBindingContext. Another option would be to implement a custom MetadataProvider for your Dictionary type, you can change the model name there as well.

OTHER TIPS

Though this question has been marked 'answered' I think the following may be helpful. I had the same problem and had a look at the source code of the System.Web.Mvc.DefaultValueProvider. It gets its values from the RouteData, the query string or from a request form submission (in that exact order). To collect all the keys (which is what you ask for in your first question) I wrote the following helper method.

private static IEnumerable<string> GetKeys(ControllerContext context)
{
    List<string> keys = new List<string>();
    HttpRequestBase request = context.HttpContext.Request;
    keys.AddRange(((IDictionary<string,
        object>)context.RouteData.Values).Keys.Cast<string>());
    keys.AddRange(request.QueryString.Keys.Cast<string>());
    keys.AddRange(request.Form.Keys.Cast<string>());
    return keys;
}

You can use this method to enumerate over the keys:

foreach (string key in GetKeys(controllerContext))
{
    // Do something with the key value.
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top