0

I'm trying to add a search form to my webpage:

@using (Html.BeginForm("CheckCourses", "Home", new { programId = ViewBag.ProgramId }, FormMethod.Get))
{
    <p class="col-md-12">
        Find by course or teacher name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
        <input type="submit" value="Search Course" class="btn-primary" />
    </p>
}

Now CheckCourses() is supposed to have a programId to run (not nullable). That's why I added new { programId = ViewBag.ProgramId } to the routeHelper. ViewBag.ProgramId is not null at the time this code runs, however, when I click on the Search Course button, I get the message that programId's value is null:The parameters dictionary contains a null entry for parameter 'programId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult CheckCourses(Int32, System.String, System.Nullable 1[System.Int32])' in 'TanulmanyiRendszer.Controllers.HomeController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter. Parameter name: parameters

Full stack trace:

[ArgumentException: The parameters dictionary contains a null entry for parameter 'programId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult CheckCourses(Int32, System.String, System.Nullable`1[System.Int32])' in 'TanulmanyiRendszer.Controllers.HomeController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters]
   System.Web.Mvc.ActionDescriptor.ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary`2 parameters, MethodInfo methodInfo) +527
   System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +91
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
   System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +22
   System.Web.Mvc.Async.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +32
   System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d() +50
   System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +225
   System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +34
   System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +26
   System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +100
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27
   System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +13
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +36
   System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +12
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +22
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +26
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
   System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +21
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +29
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +28
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9765045
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

UPDATE

Controller as requested:

public class HomeController : BaseController
{
    public ActionResult Index()
    {
        return View("Index", _programService.Programs.ToList());
    }

    public ActionResult CheckCourses(int programId, string searchString, int? page) {
        var courses = from s in _courseService.Courses select s;
        if (searchString != null)
        {
            page = 1;
        }

        ViewBag.CurrentFilter = searchString;
        ViewBag.ProgramId = programId;

        if (!String.IsNullOrEmpty(searchString))
        {
            searchString = searchString.ToLower();
            courses = courses.Where(s => s.CrsName.ToLower().Contains(searchString) || s.EducationUser.FullName.ToLower().Contains(searchString));
        }
        courses = courses.OrderBy(s => s.Id);
        int pageSize = 20;
        int pageNumber = (page ?? 1);

        return View("Courses",courses.ToPagedList(pageNumber, pageSize));
    }

}

UPDATE 2

The rendered <form> element shows the function worked well, however the issue still persist when pushing the search button:

<form action="/Home/CheckCourses?programId=2" method="get">            
    <p class="col-md-12">
        Find by course or teacher name: <input id="SearchString" name="SearchString" type="text" value="">
    <input type="submit" value="Search Course" class="btn-primary">
    </p>
</form>
Erik Philips
  • 53,428
  • 11
  • 128
  • 150
lte__
  • 7,175
  • 25
  • 74
  • 131
  • Can you also paste in the HTTPGet Controller Method for this view? – Michael Burns Apr 25 '17 at 18:53
  • @MichaelBurns I added the `CheckCourses` action controller – lte__ Apr 25 '17 at 18:56
  • try casting the programId explicitely to an int like `new { programId = (int)ViewBag.ProgramId }`. – Glia Apr 25 '17 at 18:58
  • Can you past the rendered HTML for the form element? You may also want to try and add an `html.hiddenfor<>` for programID as wll – Mad Myche Apr 25 '17 at 19:02
  • @lte__What Controller Method is used to render the form view and add the original ViewBag.ProgramId? Are you using the same method CheckCourses to both render the search form and send back the search string? – Michael Burns Apr 25 '17 at 19:04
  • @MadMyche Apparenlty the `
    ` is rendered well (see update 2).
    – lte__ Apr 25 '17 at 19:10
  • @MichaelBurns The `Courses.cshtml` view is called from another view via `@Html.ActionLink("Check courses", "CheckCourses", "Home", new { programId = Model[i].Id, currentFilter = "", searchString = "", page = 1}, new { @class = "btn btn-primary" })` and the form is in this `Courses.cshtml` – lte__ Apr 25 '17 at 19:13
  • If I manually enter `http://localhost:56272/Home/CheckCourses?programId=2&SearchString=dat` it works fine, but if I press that `Search course` button, it calls `http://localhost:56272/Home/CheckCourses?SearchString=dat` even though the form clearnly contains `programID` too... I don't get this :D – lte__ Apr 25 '17 at 19:19
  • @lte__Everything looks correct from all I can see. Can you place a breakpoint at your CheckCourses method and see what's getting sent to that method? – Michael Burns Apr 25 '17 at 19:20
  • @MichaelBurns I did, but funny thing is, `CheckCourses()` doesn't get called... – lte__ Apr 25 '17 at 19:21
  • It's def getting called, the error even return for that method CheckCourses. – Michael Burns Apr 25 '17 at 19:22
  • I've put a breakpoint in front of `public ActionResult CheckCourses(int programId, string searchString, int? page)` but nothing. Jumps right to the stack trace, not breaking... – lte__ Apr 25 '17 at 19:25

1 Answers1

0

Try moving the programId param into a hidden field:

@using (Html.BeginForm("CheckCourses", "Home", FormMethod.Get)){
    <p class="col-md-12">
        @Html.Hidden("programId", ViewBag.ProgramId)
        Find by course or teacher name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
        <input type="submit" value="Search Course" class="btn-primary" />        
    </p>
}
Michael Burns
  • 630
  • 2
  • 5
  • 10
  • Oh wow, that did it (with `int` instead of `string`). Can you explain why? – lte__ Apr 25 '17 at 19:41
  • It appeared when you hit search it was overwriting your existing programId param when you were declaring it in the BeginForm helper method. – Michael Burns Apr 25 '17 at 19:44
  • 1
    When you use FormMethod.Get, any param values are overwritten by the form data: http://stackoverflow.com/questions/1116019/submitting-a-get-form-with-query-string-params-and-hidden-params-disappear and http://stackoverflow.com/questions/3548795/html-form-why-action-cant-have-get-value-in-it – Michael Burns Apr 25 '17 at 19:53
  • I guess my comment 1/2 hour before this answer was correct: add an `html.hiddenfor<>` for programID – Mad Myche Apr 25 '17 at 20:07
  • The `(int)` shouldn't be needed as all data in a querystring is of type nullable `string`, hence the name. – Mad Myche Apr 25 '17 at 20:13