Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC passing post parameter to a dictionary

Tags:

c#

asp.net-mvc

I have MVC application that returns a ResultObjekt after processing a FormulaData object. That's a rest API called via HTTP-Post

[HttpPost]
[ActionName("GetResult")]
public ResultObjekt GetResult([FromBody]FormularData values)
{

}

Question: Is there a way to read all properties from values into a Dictionary<string, string> or a IEnumerable<KeyValuePair<string, string>> ?

e.g.

public class FormularData
{
    public string Item1 { get; set; }
    public string Item2 { get; set; }
}

should result into a Dictionary<string,string>() or a IEnumerable<KeyValuePair<string, string>>

with values { {"Item1","Value1"}, {"Item2","Value2"}}

My previous solution worked with Querystring and HttpGet instead of HttpPost and since I changed, Request.GetQueryNameValuePairs().ToDictionary(x => x.Key, x => x.Value) doesn't work anymore.


Here is my current - not so pretty solution:

[HttpPost]
[ActionName("GetResult")]
public ResultObjekt GetResult([FromBody]FormularData values)
{
    List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
    if (!string.IsNullOrEmpty(values.Item1))
    {
        list.Add(new KeyValuePair<string, string>("Item1", values.Item1));
    }
    if (!string.IsNullOrEmpty(values.Item2))
    {
        list.Add(new KeyValuePair<string, string>("Item2", values.Item2));
    }
    IEnumerable<KeyValuePair<string, string>> result = list.AsEnumerable();
}
like image 638
Toshi Avatar asked Jan 30 '17 13:01

Toshi


4 Answers

As already mentioned by many in the comments that ideally one should use a model that can hold the values from the form. This is also the cleanest way as per my opinion, as I find it more structured.

If you need to access both at the same time, first try to refactor/restructure the code if possible. In my opinion why try to access the raw form data when that is already bound to our model, considering the form data is a subset of the model data.

If refactoring/restructuring is not possible, and you need to access both, then there are couple of ways to do that.

  1. Option1: Use FormCollection:

    [HttpPost]
    public ResultObjekt GetResult(FormCollection formCol, FormularData model)
    {
        //Note that here both FormCollection and FormularData are used.
        //To get values from FormCollection use something like below:
        var item1 = formCol.Get("Item1");
    }
    
  2. Option2: Use Request.Form:

    [HttpPost]
    public ResultObjekt GetResult(FormularData model)
    {
        //To get values from Form use something like below:
        var formData = Request.Form;
        var item1 = formData.Get("Item1");
    }
    

Hope this helps.

Update: As also pointed out by Lali, whether you use FormCollection or Request.Form, you can convert that into dictionary as: formData.AllKeys.ToDictionary(k => k, v => formData[v]) (untested), as both are of type NameValueCollection

like image 52
Sayan Pal Avatar answered Nov 11 '22 00:11

Sayan Pal


While using reflection may have a performance hit you can convert the model to a dictionary using linq with reflection.

[HttpPost]
[ActionName("GetResult")]
public ResultObjekt GetResult([FromBody]FormularData values)
{
    var result = values.GetType()
                     .GetProperties()
                     .ToDictionary(pi => pi.Name, pi => (string)pi.GetValue(values));
}

performance can be improved by caching the property info returned from GetProperties. The above linq can also be converted to an extension method for reuse if desired.

like image 40
Nkosi Avatar answered Nov 10 '22 23:11

Nkosi


I think you are making your life difficult for no reason.
If you need to treat your class as a dictionary just implement an implicit operator like in the following example

If you just want to iterate on the object,you can just implement IEnumerable

using System;
using System.Collections;
using System.Collections.Generic;

namespace ConsoleApplication5
{
    class Program
    {

        static void Main(string[] args)
        {
            FormularData data = new FormularData() { Item1 = "aa", Item2 = "bb" };
            //Dictionary<string, string> dictionary = data;
            foreach (var item in data)
                Console.WriteLine(item);

            Console.ReadLine();
        }
    }

    public class FormularData : IEnumerable<KeyValuePair<string,string>>
    {
        public string Item1 { get; set; }
        public string Item2 { get; set; }

        public static implicit operator Dictionary<string, string>(FormularData obj)
        {
            var list = new Dictionary<string, string>();
            if (!string.IsNullOrEmpty(obj.Item1))
                list.Add("Item1", obj.Item1);
            if (!string.IsNullOrEmpty(obj.Item2))
                list.Add("Item2", obj.Item2);
            return list;
        }

        public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
        {
            return ((Dictionary<string, string>)this).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }
}
like image 42
George Vovos Avatar answered Nov 10 '22 23:11

George Vovos


The power of Extension methods can be used out here.

  1. Define an extension for FormularData class:

             public static class FormularDataExtensions
             {
                public static Dictionary<string, string> ConvertToDictionary(this FormularData formularData)
                {
                  var dictionary = new Dictionary<string, string>();
                  if (!string.IsNullOrEmpty(formularData.Item1))
                      dictionary.Add(nameof(formularData.Item1), formularData.Item1);
                  if (!string.IsNullOrEmpty(formularData.Item2))
                      dictionary.Add(nameof(formularData.Item2), formularData.Item2);
    
                 return dictionary;
                }
             }
    
  2. Call ConvertToDictionary() in GetResult() as:

    [HttpPost]
    [ActionName("GetResult")]
    public ResultObjekt GetResult([FromBody]FormularData values)
    {
        var dictionary = values.ConvertToDictionary();
    }
    
like image 1
Rupa Mistry Avatar answered Nov 10 '22 22:11

Rupa Mistry