user1916528
user1916528

Reputation: 389

How can I resolve null reference exception in ASP.net Core application using EF Core 3.1.5?

I'm getting a null reference exception when I add the following to the Client Details view:

<dt>
    @Html.DisplayNameFor(model => model.Contacts)
</dt>
<dd>
    <table class="table">
        <tr>
            <th>FN</th>
            <th>LN</th>
            <th>Email</th>
            <th>Contact Type</th>
        </tr>
        @foreach (var item in Model.Contacts)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.FN)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.LN)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Email)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.ContactType)
                </td>
            </tr>
        }
    </table>
</dd>

Why am I getting a null reference exception at @Html.DisplayNameFor(model => model.Contacts) and how can I fix it?

The following is the Client class with the data annotations removed to make it easier to read:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace scms_core.Models
{
    public class Client
    {        
        public int Id { get; set; }
        public string CrtdBy { get; set; }        
        public DateTime CrtdDt { get; set; }
        public string FN { get; set; }
        public string LN { get; set; }
        public string BN { get; set; }

        public int ClientTypeId { get; set; }
        public tlClientType ClientType { get; set; }

        public virtual ICollection<Contact> Contacts { get; set; }
    }
}

The following is the Contact class, also with the data annotations removed to make it easier to read:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace scms_core.Models
{
    public class Contact
    {
        public int Id { get; set; }
        public string CrtdBy { get; set; }
        public DateTime CrtdDt { get; set; }
        public string FN { get; set; }
        public string LN { get; set; }
        public string Email { get; set; }

        public int ContactTypeId { get; set; }
        public tlContactType ContactType { get; set; }

        public int ClientId { get; set; }
        public virtual Client Client { get; set; }
    }
}

When I remove the reference the code that is supposed to get the Contact information, the details page works. It just fails when I add this code to the view. Below is the Client Details View:

@model scms_core.Models.Client

@{
    ViewData["Title"] = "Details";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>Details</h1>

<div>
    <h4>Client</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.CrtdBy)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.CrtdBy)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.CrtdDt)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.CrtdDt)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.FN)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.FN)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.LN)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.LN)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.BN)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.BN)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ClientType)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ClientType.ClientType)
        </dd>

        <dt>
            @Html.DisplayNameFor(model => model.Contacts)
        </dt>
        <dd>
            <table class="table">
                <tr>
                    <th>FN</th>
                    <th>LN</th>
                    <th>Email</th>
                    <th>Contact Type</th>
                </tr>
                @foreach (var item in Model.Contacts)
                {
                    <tr>
                        <td>
                            @Html.DisplayFor(modelItem => item.FN)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.LN)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.Email)
                        </td>
                        <td>
                            @Html.DisplayFor(modelItem => item.ContactType)
                        </td>
                    </tr>
                }
            </table>
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Below is the controller code:

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

        var client = await _context.Clients
            .Include(c => c.ClientType)
            .FirstOrDefaultAsync(m => m.Id == id);

        if (client == null)
        {
            return NotFound();
        }

        return View(client);
    }

Upvotes: 0

Views: 432

Answers (2)

Alejandro H
Alejandro H

Reputation: 412

Should be like this, change Id to ClientID and make a [Key] and [Table] where you want to have primary key and making a table:

[Table("Client")]
public class Client
    {        
        [Key]
        public int ClientID { get; set; }
        public string CrtdBy { get; set; }        
        public DateTime CrtdDt { get; set; }
        public string FN { get; set; }
        public string LN { get; set; }
        public string BN { get; set; }    
        public int ClientTypeID { get; set; }
        [ForeignKey("ClientTypeID")]
        public ClientType ClientType { get; set; }
        public int ContactID { get; set; }
        [ForeignKey("ContactID")]
        public virtual Contact Contact { get; set; }   

        public ICollection<Contact> Contacts { get; set; }
    }

Change Id to ContactID

[Table("Contact")]
public class Contact
    {
        [Key]
        public int ContactID { get; set; }
        public string CrtdBy { get; set; }
        public DateTime CrtdDt { get; set; }
        public string FN { get; set; }
        public string LN { get; set; }
        public string Email { get; set; }
        public int ContactTypeID { get; set; }
        [ForeignKey("ContactTypeID")]
        public virtual ContactType ContactType { get; set; }
        public int ClientID { get; set; }
        [ForeignKey("ClientID")]
        public virtual Client Client { get; set; }
    }

Your controller should declare a list and send the view model to razor page. Should look something like this:

public async Task<ActionResult> Contacts()
    {
        var allClients = await _context.Clients.ToList();

        List<Contact> viewModel = allClients.Select(x => new Contacts
        {
            FN = x.FN,
            LN = x.LN,
            Email = x.Email,
            ContactType = x.ContactType
        }).ToList();


        return View(viewModel);
    }

You should do the same for all models that are going to create the database. In your Contacts page, you did not mention which @model you are using, but it should look something like this: @model List<YourProject.UI.Models.Contacts>

Upvotes: 0

you should initialize Contacts collection in the Client model constructor like this

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace scms_core.Models
{
  public class Client
  {        
    public Client()
    {
       Contacts = new HashSet<Contact>();
    }

    public int Id { get; set; }
    public string CrtdBy { get; set; }        
    public DateTime CrtdDt { get; set; }
    public string FN { get; set; }
    public string LN { get; set; }
    public string BN { get; set; }

    public int ClientTypeId { get; set; }
    public tlClientType ClientType { get; set; }

    public virtual ICollection<Contact> Contacts { get; set; }
}

}

Upvotes: 1

Related Questions