0

I am getting an error message that says:

Exception Details: System.InvalidOperationException: The view 'Index' or its 
master was not found or no view engine supports the searched locations. The 
following locations were searched:
~/Views/Request/Index.aspx
~/Views/Request/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
~/Views/Request/1819.master
~/Views/Shared/1819.master
~/Views/Request/1819.cshtml
~/Views/Request/1819.vbhtml
~/Views/Shared/1819.cshtml
~/Views/Shared/1819.vbhtml

I've seen several different post about this but none of those answers have solved my problem. I tried clicking through the views in visual studio and it sends me to the appropriate methods. I tried adding a home controller with a home view and an Index but that does not help as well. I tried modifying my route config and that didn't work either.

Here is my return statement that is giving me issues:

return View("Index", requestVM.AidYear);

I am calling this method:

public ActionResult Index(string aidYear)

I tried this:

return View("Index", (object)requestVM.AidYear);

and I tried this:

return View("Index", model:requestVM.AidYear);

With the last 2 I get:

System.InvalidOperationException: The model item passed into the dictionary is of type 'System.String', but this dictionary requires a model item of type 'ScholarshipDisbursement.ViewModels.Request.RequestViewModel'.

This occurs on my local machine and on our production server. The team I'm on are sure that this was working when we published it because this is a pretty big issue to not have noticed so we aren't sure why it is no longer working. No changes have been made to production since we published this application so we know no one has done something to screw it up.

Just in case this helps here is my route config:

public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Request", action = "Index", id = UrlParameter.Optional }
        );
    }

Edit: In my view I have a folder Request and inside that folder is an Index.cshtml. On that view I have a form that submits to the method SubmitScholarshipRequest. That method is on the Request Controller.

In that method I run various checks and if there is an error I add it to the ModelState. If the ModelState is invalid I then:

return View("Index", requestVM.AidYear);

Otherwise I:

return RedirectToAction("Index", "Request", new { @aidYear = requestVM.AidYear });  

Here is my view:

@using ScholarshipDisbursement.Helpers
@using ScholarshipDisbursement.ViewModels.Request
@model RequestViewModel

@{
ViewBag.Title = "Scholarship Request";
Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="requestIndexRoundedShadow">

<div class="pageTitleHeader">
    <span><img src="~/Content/img/gradCap.ico" class="headerImg" /></span>
    <span class="headerTitle"><b>Scholarship Disbursement Request</b></span>
        <hr style="border-top: 1px solid black">

@using (Html.BeginForm("SubmitScholarshipRequest", "Request", 
FormMethod.Post, new { id = "SubmitTutorRequestFrm" }))
{
    @Html.AntiForgeryToken()

    <span id="aidYearLbl" Style="font-size: 14px;font-weight: bold">Academic 
Year: </span>
    @Html.DropDownListFor(m => m.AidYear, new SelectList(Model.AidYears, 
"Code", "Name"), new { id = "aidYear", onchange = "onChangeYear()", Style = 
"font-size: 14px" })

    if (Model.Scholarships.Any())
    {
        <br />
        <br />

        <table style="width: 100%;">
            <tr>
                <th class="headerBox tableHeader" colspan="3">Scholarships 
for @Model.User.DeptName</th>
            </tr>
            <tr>
                <th class="headerBox columnHeader" style="width: 
500px;">Scholarship Name</th>
                <th class="headerBox columnHeader" style="width: 
100px;">Amount</th>
                <th class="headerBox columnHeader" style="width: 
100px;">Requested</th>
            </tr>
            @{ int i = 1; }
            @foreach (var s in Model.Scholarships)
            {
                var rowColor = i % 2 == 0 ? "E8E8E8" : "ffffff";
                <tr style="background-color: #@rowColor;">
                    <td class="rowValue">@Html.ActionLink(s.Name, 
"ScholarshipRequest", "Request", new { aidYear = s.AidYear, fundCode = s.Id, 
}, new { target = "_blank" })</td>
                    <td class="rowValue">@s.AmountTotal</td>
                    <td class="rowValue">@s.AmountRequested</td>
                </tr>
                i++;
            }
        </table>

        <br />
        <br />

        if (Model.AvailScholarships.Any())
        {
            <table style="width: 100%">
                <tr>
                    <th class="headerBox tableHeader" colspan="6">Request 
Scholarship</th>
                </tr>
                <tr>
                    <th class="headerBox columnHeader">Scholarship</th>
                    <th class="headerBox columnHeader">Banner Id</th>
                    <th class="headerBox columnHeader">Student Name</th>
                    <th class="headerBox columnHeader">Amount</th>
                    <th class="headerBox columnHeader">Term</th>
                    <th class="headerBox columnHeader">Comments</th>
                </tr>
                <tr>
                    <td class="rowValue" style="width: 200px">@Html.DropDownListFor(m => m.ScholarshipId, new SelectList(Model.AvailScholarships, "Id", "Name"), "", new { id = "scholars" })</td>
                    <td class="rowValue" style="width: 125px">@Html.TextBoxFor(m => m.StudentId, new { autocomplete = "off", Style = "width:100%", id = "bannerId", maxlength = "9" })</td>
                    <td class="rowValue" style="width: 225px"><span id="studentName"></span></td>
                    <td class="rowValue" style="width: 50px">@Html.TextBoxFor(m => m.Amount, new { autocomplete = "off", Style = "width:100%", id = "amount", Value = "", data_val_number = " " })</td>
                    <td class="rowValue" style="width: 70px">@Html.DropDownListFor(m => m.Term, new SelectList(Model.Terms, "Code", "Name"), "", new { Style = "width:70px", id = "term" })</td>
                    <td class="rowValue" style="width: auto">@Html.TextBoxFor(m => m.Comments, new { autocomplete = "off", Style = "width:100%", id = "comments" })</td>
                </tr>
                <tr>
                    <td>@Html.ValidationMessageFor(m => m.ScholarshipId)</td>
                    <td>@Html.ValidationMessageFor(m => m.StudentId)</td>
                    <td></td>
                    <td>@Html.ValidationMessageFor(m => m.Amount)</td>
                    <td>@Html.ValidationMessageFor(m => m.Term)</td>
                    <td>@Html.ValidationMessageFor(m => m.Comments)</td>
                </tr>
            </table>

            <br />
            <input type="submit" id="SubmitNomineeBtn" name="SubmitNomineeBtn" class="submitButton" value="Submit" />
            <span id="warning"></span>
            <br />
            <br />
            <div class="field-validation-error">
                @Html.ErrorFor(ViewData, "ExceedsAmountError")
            </div>
            <div class="field-validation-error">
                @Html.ErrorFor(ViewData, "DuplicateEntryError")
            </div>
            <div class="field-validation-error">
                @Html.ErrorFor(ViewData, "NullStudent")
            </div>
            <div class="field-validation-error">
                @Html.ErrorFor(ViewData, "NegativeAmount")
            </div>
        }
        else
        {
            <div style="padding-right: 100px">
                <img src="~/Content/img/alert.png" /> There are currently no funds available for the @Model.User.DeptName department.
            </div>
        }

    }
    else
    {
        <br />
        <br />
        <div style="padding-right: 100px">
            <img src="~/Content/img/alert.png" /> There are currently no scholarships available for the @Model.User.DeptName department.
        </div>
    }
}



</div>

<script>

$('#help').click(function() {
    var win = $('#window').data("kendoWindow");
    win.open();
    win.center();
    win.top();
});

$(document).ready(function () {   
    if ($('#bannerId').val()) {
        checkBannerId(); 
    }
});

function checkBannerId() {
    var student = $('#bannerId').val();
    $.ajax({
        type: "POST",
        url: '@Url.Action("GetStudentName","Request")',
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify({ bannerId: student }),  // parameter aNumber
        dataType: "json",
        success: function (msg) {
            if (msg.Result === "Null Student") {
                $('#studentName').html("Error finding student name");
                $('#studentName').css("color", "red");
                $('#bannerId').css("border-color", "red");
                $('#bannerId').css("color", "red");
                $('#warning').html("Invalid Banner Id.");
                $('#warning').css('color', 'red');

                return;
            } else {
                $('#bannerId').css("border-color", "unset");
                $('#bannerId').css("color", "black");
                $('#studentName').html(msg.Result);
                $('#studentName').css('color', 'black');
                $('#warning').html("");
                $('#warning').css('color', 'unset');
            }
        },
        error: function () {
        }
    });
}

function onChangeYear() {
    window.location = '@Url.Action("Index", "Request")?aidYear=' + $("#aidYear").val();
}

$('#amount').blur(function () {
    if (isNaN($('#amount').val()) || $('#amount').val() < 0) {
        $('#warning').html("Invalid amount ($, commas, and negative numbers not allowed).");
        $('#warning').css('color', 'red');
        $('#amount').css("border-color", "red");
        $('#amount').css('color', "red");
    } else {
        $('#amount').css("border-color", "unset");
        $('#amount').css("color", "unset");
        $('#warning').html("");
        $('#warning').css('color', 'unset');
    }
});    

$('#bannerId').blur(function () {
    checkBannerId();
});

</script>

Here is the method the form calls:

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult SubmitScholarshipRequest(RequestViewModel requestVM)
    {
        var scholarshipMgr = new ScholarshipStore();
        var scholarship = scholarshipMgr.GetScholarship(requestVM.ScholarshipId, requestVM.AidYear);
        double amountTotal = scholarship.AmountTotal;
        double amountRequested = scholarship.AmountRequested;
        double addTotal = amountRequested + requestVM.Amount;

        string username = User.Identity.Name;
        var user = new UserStore().GetUser(username);

        var student = new StudentStore().GetStudent(requestVM.StudentId);

        if (student == null)
        {
            ModelState.AddModelError("NullStudent","Unable to find Student");
        }

        var scholarshipRequestMgr = new ScholarshipRequestStore();

        var scholarshipRequest = scholarshipRequestMgr.GetScholarshipRequest(requestVM.StudentId, requestVM.ScholarshipId, requestVM.AidYear);

        if (scholarshipRequest != null)
        {
            ModelState.AddModelError("DuplicateEntryError", "Scholarship already requested for this student!");
        }

        if (addTotal > amountTotal)
        {
            ModelState.AddModelError("ExceedsAmountError", "Amount entered exceeds the total available!");
        }

        if (addTotal < 0)
        {
            ModelState.AddModelError("NegativeAmount", "Must be a positive number");
        }

        if (!ModelState.IsValid)
        {
            var aidYears = new AidYearStore().GetAidYears();
            var scholarships = new ScholarshipStore().GetScholarships(user.DeptCode, requestVM.AidYear);
            var availableScholarships = scholarships.Where(x => x.AmountTotal > x.AmountRequested);
            var terms = new TermStore().GetAllTerms();

            requestVM.AidYears = aidYears;
            requestVM.User = user;
            requestVM.Scholarships = scholarships;
            requestVM.AvailScholarships = availableScholarships;
            requestVM.Terms = terms;

            return View("Index", requestVM.AidYear);
        }

        var scholarShipRequest = new ScholarshipRequest
        {
            AidYear = requestVM.AidYear,
            ScholarshipId = requestVM.ScholarshipId,
            StudentId = requestVM.StudentId,
            Amount = requestVM.Amount,
            TermCode = requestVM.Term,
            Comments = requestVM.Comments,
            DeptId = user.DeptCode,
            DateCreated = DateTime.Now,
            CreatedBy = username,
            PIDM = new StudentStore().GetStudent(requestVM.StudentId).PIDM
        };

        new ScholarshipRequestStore().CreateScholarshipRequest(scholarShipRequest);

        return RedirectToAction("Index", "Request", new { @aidYear = requestVM.AidYear });

    }

Here is the Index View Method from the RequestController:

public ActionResult Index(string aidYear)
    {
        aidYear = string.IsNullOrEmpty(aidYear) ? new FinAidYear().GetCurrentYear() : aidYear;

        string username = User.Identity.Name;
        if (!Authorization.CheckUsernameDept(username))           
            return RedirectToAction("NotAuthorized", "Account");

        var user = new UserStore().GetUser(username);
        var aidYears = new AidYearStore().GetAidYears();
        var scholarships = new ScholarshipStore().GetScholarships(user.DeptCode, aidYear);
        var availableScholarships = scholarships.Where(x => x.AmountTotal > x.AmountRequested);
        var terms = new TermStore().GetAllTerms();

        var requestVM = new RequestViewModel
        {
            AidYear = aidYear,
            AidYears = aidYears,
            User = user,
            Scholarships = scholarships,
            AvailScholarships = availableScholarships,
            Terms = terms
        };

        return View(requestVM);
    }
ADyson
  • 57,178
  • 14
  • 51
  • 63
smuldr
  • 315
  • 1
  • 12
  • can you show us the code for the view you're trying to display via the non-working `return` statement, what its file name is, and where in your folder structure it is? – ADyson Dec 17 '18 at 20:01
  • You do understand that a `return` statement to return a view just loads the view directly? It doesn't call an action method. So the object you return to it must be the same type as the model in the view file. – ADyson Dec 17 '18 at 20:04
  • I added more code to my original post. – smuldr Dec 17 '18 at 20:07
  • I tried doing what @Phteven said in his post but that didn't work for me either. – smuldr Dec 17 '18 at 20:08
  • didn't work how, exactly? What goes wrong? It sounds like that should be exactly what you do, because the Index view expects a RequestViewModel to be given to it, not a string. – ADyson Dec 17 '18 at 20:11
  • Perhaps it should be `return View("Index", requestVM);` but it should certainly be `requestVM` which you send, not just the year – ADyson Dec 17 '18 at 20:17
  • even though sometimes when I load that page it won't have anything on the model besides the year? – smuldr Dec 17 '18 at 20:28
  • Yes because you can't just send a completely different type of data to what the page is expecting. When you run your "Index()" action method, it eventually returns a whole VM object to the view, doesn't it? Same must be true here. The year will be within your model, so if it wants that it'll use it. Besides, in this case you're dealing with a specific condition where an error has occurred, so what you want is for the view to re-display everything the user just entered, and highlight the error(s) so they can correct them. You don't want them to have to type everything in again, do you? – ADyson Dec 17 '18 at 20:29
  • OK thanks. I guess this will be more work than I had anticipated. Thanks for the help. – smuldr Dec 17 '18 at 20:36
  • It should't be, if your view is set up normally - it'll just read the values back into the fields, and display the validation message(s). Admittedly I haven't spent long looking at your view to see if that's the case, but that's the concept in MVC. P.S. I posted an answer - if it resolves this particular error, then please remember to mark it accepted, thanks. I notice you have accepted/upvoted comparatively few of the answers to your earlier questions (including [this one](https://stackoverflow.com/questions/53746422/mvc-shared-layout-between-projects) where I posted an answer too) – ADyson Dec 17 '18 at 20:43
  • I try to mark things as answered as often as I can. The shared layout question I haven't gotten to test yet because this bug came up so I had to switch gears. Once I get a chance though however I will take a look at the shared layout answer you gave and let you know how that goes. Thanks for your help. – smuldr Dec 17 '18 at 20:47
  • Possible duplicate of [The model item passed into the dictionary is of type .. but this dictionary requires a model item of type](https://stackoverflow.com/questions/40373595/the-model-item-passed-into-the-dictionary-is-of-type-but-this-dictionary-requ) – Tetsuya Yamamoto Dec 18 '18 at 01:01

3 Answers3

1

It seems your view has the header :

@model ScholarshipDisbursement.ViewModels.Request.RequestViewModel

So, you should use that :

return View(requestVM);

For the application is expected all the viewModel.

Otherwise, we will need your structure for a better help.

Phteven
  • 47
  • 6
  • I added more to my original post – smuldr Dec 17 '18 at 16:25
  • I think it should be `return View("Index", requestVM);` because during this execution we're not in the "Index" method - see the updated code. But yes you're right to say it should be the viewmodel that's returned. – ADyson Dec 17 '18 at 20:17
1

Your view expects a RequestViewModel, not a string. So giving it just the aid year will not work. That's what the "Index" action method expects, but here we're just loading the view directly, not executing the action method (after all, we're already executing a different action method). So you need to pass the whole viewmodel object in the return statement.

Additionally, because it's not an action method called "Index", you'll need to specify the name of the view you want to return.

I would expect that

return View("Index", requestVM);

would solve your problem.

ADyson
  • 57,178
  • 14
  • 51
  • 63
  • Ok so this works for me, however when I load the page up my validation is triggered even though I haven't run anything yet. – smuldr Dec 18 '18 at 13:19
  • @crmckain We're talking about the `return` statement inside the `if (!ModelState.IsValid)` block, yes? So you must have given it a model to validate...and it wasn't valid. Which is why you'd get validation errors. I can't see why you'd call this particular action with an empty model...if you want to do that, you could just call the Index() action method instead. – ADyson Dec 18 '18 at 13:39
  • No that return statement is working great. I'm talking about when I first load the Index page before I even submit a form. – smuldr Dec 18 '18 at 13:46
  • Right but that has nothing to do with the original question, or this answer...that's in another part of your code. It's a separate issue. From a brief glance I'd guess it's maybe something to do with the `@Html.ErrorFor` sections and not setting the viewdata properly, but you'd have to ask a new question about it really, to get some detailed attention it. – ADyson Dec 18 '18 at 13:48
0

I think the clue is in the error message. It's looking for a view named 1819.cshtml.

This is telling it to look for a view with the value of requestVM.AidYear to use for the master or template page.

return View("Index", requestVM.AidYear);

If you wanted to pass a model to the view use this instead. Instead of a string (which I assume requestVM.AidYear is) I'm passing something that's not a string. So it'll use requestVM as the model.

return View("Index", requestVM);

Alternatively, this may be what you want. to use

return RedirectToAction("Index", "Request", new { @aidYear = requestVM.AidYear });
Skye MacMaster
  • 894
  • 1
  • 10
  • 19