0

I have the following situation:

  • A user can post a comment on a order
  • to display the comment form, I am calling @{Html.RenderAction("CreateComment", "Order", new { orderId = Model.Id, orgId = Model.OrganisationId });} on the Details view for the order
  • Once the user posts their comment, I need to redirect back to the Details view of the order
  • However, when calling RedirectToAction, I get :

    Child actions are not allowed to perform redirect actions.

To resolve this, I tried calling this insead of Redirect:

return View("Details", new { id = viewModel.OrderId });

But now I'm seeing this weird error:

The model item passed into the dictionary is of type '<>f__AnonymousType4`1[System.Int32]', but this dictionary requires a model item of type 'ViewOrderViewModel'.

Which seems like it's complaining that I am not passing a ViewModel to the view.

However, if you look at the Details controller action which I am callilng, it actually accepts an int and builds the ViewModel there (I can see that this action is still hit after making the above change):

public ActionResult Details(int id)
{
    var orderViewModel = orderManager.Select(id);
    return View(orderViewModel);
}

What is going on here? Why can't I just call the actions using the id route value, as I normally do? What is happening to my ViewOrderViewModel?

Any advice is much appreciated thank you.


Update to add code

Order Details.cshtml

This is where I am dispalying the comments form, and where I need to redirect to at the end of all this. The url looks loks ~/order/details/14.

The error seems to be thrown on the line @{Html.RenderAction("CreateComment", "Order", new { orderId = Model.Id, orgId = Model.OrganisationId });}:

Child actions are not allowed to perform redirect actions.

@using Web.Resources.Headers

@model Web.Models.ViewOrderViewModel

<div class="container">
    <h4>@Model.Name</h4>
    <hr />
    <div class="col-xs-12">

        <h4>Comments</h4>
        <hr />

        <div class="col-xs-12 col-sm-6">
            @foreach (var comment in Model.Comments)
            {
                @comment.Content
            }
        </div>

        <div class="col-xs-12 col-sm-6">
            @{Html.RenderAction("CreateComment", "Order", new { orderId = Model.Id, orgId = Model.OrganisationId });}
        </div>
    </div>
</div>

CreateComment.cshtml

The form to post a comment

@using Microsoft.AspNet.Identity

@model Web.Models.NewCommentViewModel

@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

        @Html.HiddenFor(model => model.OrderId)
        @Html.HiddenFor(model => model.OrganisationId)
        @Html.HiddenFor(model => model.UserId)

        <div class="form-group has-feedback">
            <div class="col-xs-12" style="padding:0px;">
                @Html.TextAreaFor(model => model.Content, new { @class = "form-control", placeholder = Html.DisplayNameFor(model => model.Content) })
                <span class="glyphicon glyphicon-option-horizontal form-control-feedback"></span>
            </div>
        </div>

        <div class="form-group">
            <div class="pull-right">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

OrderController

Here you can see the controller methods used for dispalying the comment form and posting the comment to the database ('Redirect' method failed with the same error about child actions):

public ActionResult CreateComment(int orderId, int orgId)
{
    var viewModel = new NewCommentViewModel()
    {
        OrderId = orderId,
        OrganisationId = orgId,
        UserId = User.Identity.GetUserId()
    };
    return PartialView(viewModel);
}

[HttpPost]
public ActionResult CreateComment(NewCommentViewModel viewModel)
{
    orderManager.AddComment(viewModel);
    return RedirectToAction("Details", new { id = viewModel.OrderId });
    //return Redirect($"/Order/Details/{viewModel.OrderId}");
}
Bassie
  • 9,529
  • 8
  • 68
  • 159
  • As for the second issue, its because your `Details` view expects a model which is `ViewOrderViewModel` but all you passing is an anonymous object (`new { id = viewModel.OrderId }`). You need to return a instance of `ViewOrderViewModel` –  Oct 06 '17 at 03:33
  • `return View("Details", new { id = viewModel.OrderId });` => this should be return viewmodel instance, e.g. `return View("Details", viewModel);`. Also you can try to use `Redirect` instead of `RedirectToAction` (https://stackoverflow.com/questions/2056421/why-are-redirect-results-not-allowed-in-child-actions-in-asp-net-mvc-2). – Tetsuya Yamamoto Oct 06 '17 at 03:35
  • @StephenMuecke But when I step through the `Details` method, I can see that a `ViewModel` is built, and is passed into the `View` in the line `return View(orderViewmodel);`, doesn't that mean I am passing the correct object to the view at the end of all this? – Bassie Oct 06 '17 at 03:35
  • No its `return View("Details", new { id = viewModel.OrderId });` that is the problem - the 2nd parameter is the model and your passing a model which is an anonymous object (containing one property named `id`), not an instance of `ViewOrderViewModel` as you did in the GET method –  Oct 06 '17 at 03:37
  • As for the first issue, you need to give a bit more detail, including the code –  Oct 06 '17 at 03:37
  • I don't see any `RedirectToAction` section in your example, can you add it? Note that *you can't redirect to load partial view* using `Redirect` or `RedirectToAction`. – Tetsuya Yamamoto Oct 06 '17 at 03:41
  • @TetsuyaYamamoto I added a bunch of code and some explanation, please let me know if I can provide anything else and thanks for your help – Bassie Oct 06 '17 at 03:48
  • @StephenMuecke I updated the question with my code - let me know if it is not enough – Bassie Oct 06 '17 at 03:49
  • 1
    Not related, but its just `@Html.HiddenFor(m => m.OrderId)` etc (no 2nd argument) –  Oct 06 '17 at 03:52
  • @StephenMuecke thanks stephen I updated – Bassie Oct 06 '17 at 03:53

3 Answers3

1

@{Html.RenderAction("CreateComment", ...);} may be calling your overload.

Try renaming:

public PartialViewResult _CreateComment(int orderId, int orgId)
{
    var viewModel = new NewCommentViewModel()
    {
        OrderId = orderId,
        OrganisationId = orgId,
        UserId = User.Identity.GetUserId()
    };
    return PartialView(viewModel);
}

And calling:

@{Html.RenderAction("_CreateComment", ...);}
aaron
  • 39,695
  • 6
  • 46
  • 102
1

First, try to set BeginForm in partial view form to include controller & action method name with POST HTTP method like this:

@using (Html.BeginForm("CreateComment", "Order", FormMethod.Post))
{
    // view controls
} 

Also try assigning GET action method with different name than POST action method name and include ChildActionOnlyAttribute like this:

[ChildActionOnly]
public ActionResult RenderCreateComment(int orderId, int orgId)
{
    var viewModel = new NewCommentViewModel()
    {
        OrderId = orderId,
        OrganisationId = orgId,
        UserId = User.Identity.GetUserId()
    };
    return PartialView(viewModel);
}

And then assign the renamed action in RenderAction:

@{Html.RenderAction("RenderCreateComment", "Order", new { ... });}

Note that a child action supposed to populate or getting data, it shouldn't attempting to update something else.

Similar issues:

Child actions are not allowed to perform redirect actions

Child actions are not allowed to perform redirect actions MVC4

Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61
  • This was the first answer which solved the issue. Specifically, all i needed to do was update the `BeginForm` parameters. Thanks! – Bassie Oct 06 '17 at 04:29
1

The issue is that you form is generated as a partial from a child action and is you inspect the html for the <form> element you will see that it generated action="/order/details/14" whereas it need to be action="/order/CreateComment"

You need to change the BeginForm() method to generate the correct attribute so it POST to the correct method. In your CreateComment.cshtml view

@model Web.Models.NewCommentViewModel
@using (Html.BeginForm("CreateComment", "Order")) // change
{
    @Html.AntiForgeryToken()
    ....