9

I wanted to create a method in my Base controller that would take a list and return it as a SelectList. I wrote a method, and the site compiles, but I get this message:

Cannot call action method 'System.Collections.Generic.IEnumerable1[System.Web.Mvc.SelectListItem] GetSelectList[T](System.Collections.Generic.IEnumerable1[T], System.String, System.String, System.String, System.Object)' on controller 'PublicationSystem.Controllers.BaseController' because the action method is a generic method.
Parameter name: methodInfo

I'm wondering what I did wrong. Here's the code:

public partial class BaseController : Controller
{
    public IEnumerable<SelectListItem> GetSelectList<T>
    (
        IEnumerable<T> itemList, 
        string textField,
        string valueField,
        string defaultPrompt = "", 
        object defaultValue = null)
    {
        IEnumerable<SelectListItem> returnList = null;

        if (!string.IsNullOrEmpty(defaultPrompt))
        {
            returnList = Enumerable.Repeat(
                new SelectListItem { Value = (string)defaultValue, Text = defaultPrompt },
                count: 1);
        }

        var textProp = typeof (T).GetProperty(textField);
        var valueProp = typeof (T).GetProperty(valueField);

        returnList = returnList.Concat
            (itemList
                .Select(x =>
                    new SelectListItem
                    {
                        Value = Convert.ToString(valueProp.GetValue(x)),
                        Text = Convert.ToString(textProp.GetValue(x)),
                    }).Distinct().ToList());

        return returnList.ToList();
    }
}

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.LowercaseUrls = true;
        routes.MapMvcAttributeRoutes(); // Errors here
        //..
    }
}
M Kenyon II
  • 4,136
  • 4
  • 46
  • 94

2 Answers2

25

Your method is public in controller class. Asp.Net will see this method as an action method.

You can't use generic methods for action. Check this.

If you want to use this method on derived classes(your controllers). Make your method protected.

Community
  • 1
  • 1
Erkan Demirel
  • 4,302
  • 1
  • 25
  • 43
  • 2
    'Asp.Net will see this method as an action method.' was the key point, thank you. That sheds a new light on how the inherited controller works and what should and shouldn't be there. – M Kenyon II Mar 04 '16 at 20:46
  • @MKenyonII If it helped you can mark as an answer to guide other people. – Erkan Demirel Mar 06 '16 at 17:18
  • I get this error even when decorating the method as [NonAction], which is annoying as that can make unit testing a bit more tedious, but then I take the hint and move it to a utility class instead which is where it belongs. – Etherman Jan 05 '23 at 16:46
  • @Etherman don't write unit test for controller. That means don't do any business other than orchestrating at controller level. – Erkan Demirel Jan 05 '23 at 19:53
0

I removed this method. I turned it into an Extension method for IEnumerable<T>. Here's what I'm doing now:

public static class EnumerableExtensions
{
    public static IEnumerable<SelectListItem> AsSelectList<T>
    (
        this IEnumerable<T> dataList,
        string textField,
        string valueField,
        string defaultPrompt = "",
        object defaultValue = null)
    {
        IEnumerable<SelectListItem> returnList = new List<SelectListItem>();

        if (!string.IsNullOrEmpty(defaultPrompt))
        {
            returnList = Enumerable.Repeat(
                new SelectListItem { Value = (string)defaultValue, Text = defaultPrompt },
                count: 1);
        }

        var textProp = typeof(T).GetProperty(textField);
        var valueProp = typeof(T).GetProperty(valueField);

        returnList = returnList.Concat
            (dataList
                .Select(x =>
                    new SelectListItem
                    {
                        Value = Convert.ToString(valueProp.GetValue(x)),//x["valueField"],
                        Text = Convert.ToString(textProp.GetValue(x)),//x.["textField"]
                    }).Distinct().ToList());

        return returnList;
    }
}
M Kenyon II
  • 4,136
  • 4
  • 46
  • 94