2

I am experiencing a bizarre problem with RenderAction. As my view model is very complex I will try to streamline the process in broad strokes, as follows:

  1. Browser requests controller action.
  2. Action populates complex view model and passes to a view
  3. View contains a renderAction to build child/partial view. (I uses renderAction instead of a partialView at this point due to the complexity of the model the partial view needs.)
  4. This proceeds as necessary and the view is shown in the browser without error.
  5. If I create a model error after posting back some bad data and then return the same model to the same view, an error is thrown when the renderAction is called. When debugging, the controller is accessed, but the action is skipped and the app goes directly to disposing at the bottom of the controller.

It would seem that the only difference is that main controller action (not the partial) that populates the big view model is at first reached via a get and then fails when reached via a post. I played around for hours with the model - even recreated the model from scratch - so it can't be related to the model.

Here is the stack trace:

    at System.Web.HttpServerUtility.ExecuteInternal(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage, VirtualPath path, VirtualPath filePath, String physPath, Exception error, String queryStringOverride)
   at System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage)
   at System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm)
   at System.Web.HttpServerUtilityWrapper.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm)
   at System.Web.Mvc.Html.ChildActionExtensions.ActionHelper(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues, TextWriter textWriter)
   at System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper htmlHelper, String actionName, Object routeValues)
   at ASP._Page_Views_Pricelist__drawProductPricelistProductRow_cshtml.Execute() in c:\Users\Administrator\Documents\ProofPix_TFS\ProofPix\ProofPixAdmin\Views\Pricelist\_drawProductPricelistProductRow.cshtml:line 35
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
   at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
   at System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance)
   at System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer)
   at System.Web.Mvc.HtmlHelper.RenderPartialInternal(String partialViewName, ViewDataDictionary viewData, Object model, TextWriter writer, ViewEngineCollection viewEngineCollection)
   at System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper htmlHelper, String partialViewName, Object model)
   at ASP._Page_Views_Pricelist_edit_cshtml.Execute() in c:\Users\Administrator\Documents\ProofPix_TFS\ProofPix\ProofPixAdmin\Views\Pricelist\Edit.cshtml:line 121
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
   at System.Web.Mvc.WebViewPage.ExecutePageHierarchy()
   at System.Web.WebPages.StartPage.RunPage()
   at System.Web.WebPages.StartPage.ExecutePageHierarchy()
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)
   at System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance)
   at System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer)
   at System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass1a.<InvokeActionResultWithFilters>b__17()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation)

Here is the renderAction:

Html.RenderAction("_pricelistProductOptions", new { id = Model.Product.ProductId, ShowHtml = false });

You're help is greatly appreciated. BTW (kinda a newb)!

EDIT (added action)

        // POST: /Pricelist/Edit/5

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(adminEditPricelistVM adminEditPricelistVM)
    {

        if (ModelState.IsValid)
        {
            //Code removed for simplicity

            return RedirectToAction("Edit", new { id = pricelist.PricelistId });
        }

        int vendorId = (int)adminEditPricelistVM.VendorId;
        Vendor vendor = (from b in db.Vendors where b.VendorId == vendorId select b).SingleOrDefault();

        adminEditPricelistVM.ProductCategories = (from a in db.ProductCategories
                                                  from b in db.VendorProductCategory
                                                  where b.VendorId == vendorId && a.ProductCategoryId == b.ProductCategoryId
                                                  select a).OrderBy(o => o.SortOrder).ToList();

        List<Product> products = (from a in db.Products where a.DiscontinuedDate == null && a.VendorId == 1 select a).OrderBy(o => o.SortOrder).ToList();

        //repopulate ProductCategory and Vendor nav properties in the Formula items as these are no longer populated after post
        foreach(PricingFormula pf in adminEditPricelistVM.PricingFormulas){
            pf.ProductCategory = (from a in adminEditPricelistVM.ProductCategories where a.ProductCategoryId == pf.ProductCategoryId select a).SingleOrDefault();
            pf.Vendor = vendor;
        }


        adminEditPricelistVM.Pricelist.PricingFormulas = new List<PricingFormula>();
        adminEditPricelistVM.Pricelist.PricingFormulas.AddRange(adminEditPricelistVM.PricingFormulas);

        List<PricelistProduct> thisFilteredPP = (from a in adminEditPricelistVM.Pricelist.PricelistProducts where a.ProductId > 0 select a).ToList();
        List<PricelistProductOption> thisOptionsToDelete = new List<PricelistProductOption>();
        List<PricelistProductOptionsDetail> thisOptionDetailsToDelete = new List<PricelistProductOptionsDetail>();

        //filter pricelistProducts so only selected options remain in list
        foreach (PricelistProduct pp in thisFilteredPP)
        {
            pp.PricelistId = adminEditPricelistVM.Pricelist.PricelistId;
            var x = pp.ProductId;


            foreach (PricelistProductOption ppo in pp.PricelistProductOptions)
            {
                //repopulate PricelistProduct object
                ppo.PricelistProduct = pp;
                ppo.PricelistProductId = pp.PricelistProductId;

                int numPODs = (from a in ppo.PricelistProductOptionsDetails where a.ProductOptionsDetailId > 0 select a).Count();

                if (numPODs == 0)
                {
                    thisOptionsToDelete.Add(ppo);

                }
                else
                {

                    foreach (PricelistProductOptionsDetail ppod in ppo.PricelistProductOptionsDetails)
                    {
                        //repopulate  PricelistProductOption object
                        ppod.PricelistProductOption = ppo;
                        ppod.PricelistProductOptionsId = ppo.PricelistProductOptionId;

                        if (ppod.ProductOptionsDetailId == 0)
                        {
                            thisOptionDetailsToDelete.Add(ppod);
                        }
                        else //POD is selected but if it is the default option and it is the only option and it is priced at 0.00, then we will remove it to as it is the default setting.
                        {

                            if (ppod.Price == 0 && numPODs == 1)
                            {
                                ProductOptionsDetail prodOpDet = (from c in db.ProductOptionsDetails where c.ProductOptionsDetailId == ppod.ProductOptionsDetailId select c).SingleOrDefault();
                                if (prodOpDet.IsDefault == true)
                                {
                                    thisOptionsToDelete.Add(ppo);
                                }
                            }
                        }
                    }

                    foreach (PricelistProductOptionsDetail dppod in thisOptionDetailsToDelete)
                    {
                        ppo.PricelistProductOptionsDetails.Remove(dppod);
                    }
                    thisOptionDetailsToDelete.Clear();
                }
            }

            foreach (PricelistProductOption dppo in thisOptionsToDelete)
            {
                pp.PricelistProductOptions.Remove(dppo);
            }
            thisOptionsToDelete.Clear();
        }

        adminEditPricelistVM.Pricelist.PricelistProducts = new List<PricelistProduct>();
        adminEditPricelistVM.Pricelist.PricelistProducts.AddRange(thisFilteredPP);

        adminEditPricelistVM.PPPVMs =
               (from product in products
                join pricelistProduct in adminEditPricelistVM.Pricelist.PricelistProducts on product.ProductId equals pricelistProduct.ProductId into gj
                from subpricelistProduct in gj.DefaultIfEmpty()
                select new adminEditProductsPricelistProductsVM()
                {
                    CategoryId = (int)product.ProductCategoryId,
                    Product = product,
                    PricelistProduct = subpricelistProduct
                }).ToList();

        //repopulate PricelistProducts.Product and PricelistProducts.ProductCategory in Pricelist
        foreach (PricelistProduct pp in adminEditPricelistVM.Pricelist.PricelistProducts)
        {
            pp.Product = (from p in products where p.ProductId == pp.ProductId select p).SingleOrDefault();
            pp.ProductCategory = (from a in adminEditPricelistVM.ProductCategories where a.ProductCategoryId == pp.ProductCategoryId select a).SingleOrDefault();
            pp.Pricelist = adminEditPricelistVM.Pricelist;
        }

        ViewBag.PricingFormulaRoundingTypes = (from c in db.PricingFormulaRoundingTypes select c).ToList();

        var errors = ModelState.Select(x => x.Value.Errors).ToList();
        return View(adminEditPricelistVM);

    }
Julian Dormon
  • 1,767
  • 4
  • 32
  • 58
  • How about showing us at least the action methods, especially the signatures? – JTMon May 27 '13 at 13:29
  • What is the error being thrown? Is Model.Product null? is its id null? You need to provide more code as I have no idea what action method is called on the first run (in point 1). You don't need to provide the methods just the signatures and any redirects, returns, etc. and the important parts of the view. – JTMon May 28 '13 at 06:33

2 Answers2

0

This answer is just a hunge, as the actual error isn't showing, just the stacktrace.

//repopulate PricelistProducts.Product and PricelistProducts.ProductCategory in Pricelist
foreach (PricelistProduct pp in adminEditPricelistVM.Pricelist.PricelistProducts)
{
    pp.Product = (from p in products where p.ProductId == pp.ProductId select p).SingleOrDefault();

There are probably more products in your PricelistProducts, with a ProductId that doesn't match with any of the products in your products list, resulting in pp.Product being set to null.

When rendering your action for each object in the Model.PricelistProducts with

html.RenderAction("_pricelistProductOptions", new { id = Model.Product.ProductId, ShowHtml = false });

it will (probably) throw a System.NullReferenceException: Object reference not set to an instance of an object. as you can't call Model.Product.ProductId, since Model.Product is null.

Major Byte
  • 4,101
  • 3
  • 26
  • 33
  • Thanks but that is not it. As I mentioned, I populated the model from scratch and it still failed. In other words I passed the same model that works successfully when using the get method. – Julian Dormon Jun 03 '13 at 17:55
  • Then the question is, what is at line 35 in the view `_drawProductPricelistProductRow.cshtml` – Major Byte Jun 03 '13 at 19:23
0

You are using Razor engine there (*.cshtml files)? In that case you have to use:

@Html.Action("actionName", "controllerName", new { id="myId" })

and, it seems you put name of the view there "_pricelistProductOptions", instead of action. I doubt that you have: public ActionResult _pricelistProductOptions(....) right?

Nenad
  • 24,809
  • 11
  • 75
  • 93
  • Thanks. Yes it is C# razor. That is the name of the action as well as the view. And this all works perfectly when passing the model from a get request. – Julian Dormon Jun 03 '13 at 18:24
  • THEN, you most likely manipulate your IDs in the View and screw up binding. Can we see exact MODEL and view rendering? – Nenad Jun 03 '13 at 21:34