15

My controller method looks like this:

public ActionResult SomeMethod(Dictionary<int, string> model)
{

}

Is it possible to call this method and populate "model" using only query string? I mean, typing something like this:

ControllerName/SomeMethod?model.0=someText&model.1=someOtherText

in our browser address bar. Is it possible?

EDIT:

It would appear my question was misunderstood - I want to bind the query string, so that the Dictionary method parameter is populated automatically. In other words - I don't want to manually create the dictionary inside my method, but have some automathic .NET binder do it form me, so I can access it right away like this:

public ActionResult SomeMethod(Dictionary<int, string> model)
{
    var a = model[SomeKey];
}

Is there an automatic binder, smart enough to do this?

user2384366
  • 1,034
  • 2
  • 12
  • 28
  • Look this : http://stackoverflow.com/questions/2375372/is-there-a-way-to-get-all-the-querystring-name-value-pairs-into-a-collection – Portekoi Mar 28 '14 at 08:44
  • @max that also didn't really improve readability. – CodeCaster Mar 28 '14 at 08:50
  • @CodeCaster It was better than before, since it didn't look like it was part of the question, but more like an example of how someone else achieved it. What you did now is better. – Max Mar 28 '14 at 08:52

4 Answers4

20

In ASP.NET Core, you can use the following syntax (without needing a custom binder):

?dictionaryVariableName[KEY]=VALUE

Assuming you had this as your method:

public ActionResult SomeMethod([FromQuery] Dictionary<int, string> model)

And then called the following URL:

?model[0]=firstString&model[1]=secondString

Your dictionary would then be automatically populated. With values:

(0, "firstString")
(1, "secondString")
tbfa
  • 489
  • 4
  • 12
  • What would a `route` attribute look like for something like this? – Storm Muller Sep 18 '18 at 19:46
  • If you have other query params, add `[FromQuery(Name = "model")]` attribute to make sure that the model attribute doesn't capture other query parameters if no query parameter such as `model[key]=value` is passed. – dinesh ygv Jan 12 '22 at 16:04
11

For .NET Core 2.1, you can do this very easily.

public class SomeController : ControllerBase
{
    public IActionResult Method([FromQuery]IDictionary<int, string> query)
    {
        // Do something
    }
}

And the url

/Some/Method?1=value1&2=value2&3=value3

It will bind that to the dictionary. You don't even have to use the parameter name query.

Todd Skelton
  • 6,839
  • 3
  • 36
  • 48
1

try custom model binder

      public class QueryStringToDictionaryBinder: IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var collection = controllerContext.HttpContext.Request.QueryString;
        var modelKeys =
            collection.AllKeys.Where(
                m => m.StartsWith(bindingContext.ModelName));
        var dictionary = new Dictionary<int, string>();

        foreach (string key in modelKeys)
        {
            var splits = key.Split(new[]{'.'}, StringSplitOptions.RemoveEmptyEntries);
            int nummericKey = -1;
            if(splits.Count() > 1)
            {
                var tempKey = splits[1]; 
                if(int.TryParse(tempKey, out nummericKey))
                {
                    dictionary.Add(nummericKey, collection[key]);    
                }   
            }                 
        }

        return dictionary;
    }
}

in controller action use it on model

     public ActionResult SomeMethod(
        [ModelBinder(typeof(QueryStringToDictionaryBinder))]
        Dictionary<int, string> model)
    {

        //return Content("Test");
    }
1

More specific to mvc model binding is to construct the query string as

/somemethod?model[0].Key=1&model[0].Value=One&model[1].Key=2&model[1].Value=Two

Custom Binder would simply follow DefaultModelBinder

   public class QueryStringToDictionary<TKey, TValue> : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var modelBindingContext = new ModelBindingContext
        {

            ModelName = bindingContext.ModelName,
            ModelMetadata = new ModelMetadata(new EmptyModelMetadataProvider(), null, 
                null, typeof(Dictionary<TKey, TValue>), bindingContext.ModelName),
            ValueProvider = new QueryStringValueProvider(controllerContext)
        };

        var temp = new DefaultModelBinder().BindModel(controllerContext, modelBindingContext);

        return temp;
    }
}

Apply custom model binder in model as

     public ActionResult SomeMethod(
        [ModelBinder(typeof(QueryStringToDictionary<int, string>))] Dictionary<int, string> model)
    {
       // var a = model[SomeKey];
        return Content("Test");
    }