2
votes

I'm currently learning ASP.net Core 2.0, specifically Razor pages. I'm currently trying to understand partial views.

I have a page, ViewReport, which essentially shows the data for a report and allows the user to edit it. The model looks like this:

    namespace ReportControlPanel.Pages
{
    public class ViewReportModel : PageModel
    {
        private readonly ReportControlPanel.Data.MainContext _context;

        public ViewReportModel(ReportControlPanel.Data.MainContext context)
        {
            _context = context;
        }

        [BindProperty]
        public Reports Reports { get; set; }
        [BindProperty]
        public ExecRptModel ExecRptModel { get; set; }

        public async Task<IActionResult> OnGetAsync(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            Reports = await _context.ReportContents
                .SingleOrDefaultAsync(m => m.ID == id);


            if (Reports == null)
            {
                return NotFound();
            }
            return Page();

        }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Attach(Reports).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {

            }

            return RedirectToPage("./Index");
        }
    }
}

I tried to create a partial view, ExecRpt, which would show report excution logs for that report. The model looks like this:

namespace ReportControlPanel.Pages
{
    public class ExecRptModel
    {
        private readonly ReportControlPanel.Data.MainContext _context;

        public ExecRptModel(ReportControlPanel.Data.MainContext context)
        {
            _context = context;
        }

        public IList<Logs> Logs { get; set; }

        public async Task OnGetAsync()
        {
            IQueryable<Logs> LogsIQ = from i in _context.vwReportLogs select i;

            Logs = await LogsIQ.ToListAsync();

        }
    }
}

I'm rendering the partial view in ViewReport.cshtml like this:

@await Html.PartialAsync("ExecRpt", Model.ExecRptModel)

It took me a while to get that working without erroring.

Then I moved onto the markup of the partial view. Once I did this to generate the list into HTML:

@foreach (var item in Model.Logs)
{
    <p>@Html.DisplayFor(i => item.ID)</p> //etc etc
}

I am getting the confusing error on debug.

NullReferenceException: Object reference not set to an instance of an object.

The error is highlighting the @foreach block in my partial view model.

Now I know the Logs model should return a load of data, because this works fine when not being called from a partial view.

Please can someone point me in the right direction?

1
Do you have your ExecRptModel model declared in your view? You might want to initialize Logs as an empty list at start up, since the loading might by asynchronous.EzLo

1 Answers

0
votes

Your class ExecRptModel contains a method called OnGetAsync(). This is a pattern employed by PageModel-deriving classes (i.e. the code-behind for Razor Pages). It is not available to Partial Views. I'm sure you can still call OnGetAsync() just like any other regular method, but don't expect it to be called automatically when the partial view is first rendered (try setting a breakpoint and seeing if any of the code there gets executed). Therefore, I suspect you're getting a null reference exception because Logs is never set to anything, so it is null when you try to iterate over it in the view.

I recommend moving the logic that fetches Logs into the constructor for ExecRptModel. Or, consider using View Components, which will allow you to execute more complex data-fetching logic when the component is rendered.