Home > other >  How are MVC Data models populated?
How are MVC Data models populated?

Time:10-12

I'm working with an ASP.NET 6 MVC Application and have this method to retrieve the "Add" view in my controller.

        // GET: Operations/Bookings/Add
        public IActionResult Add()
        {
            ViewData["ClientList"] = _context.Clients.ToList<Client>();
            ViewData["CountryList"] = _context.Countries.ToList<Country>();

            return View();
        }

We use ViewData["ClientList"] to retrieve the complete list of clients for the user to choose which one to associate the new "booking" record with:

Screenshot

<fieldset>
    <div >
        <div >
            <table >
                <thead>
                    <tr>
                        <th>Name</th>
                        <th style="width: 250px">City</th>
                        <th style="width: 250px">Country</th>
                    </tr>
                </thead>
                <tbody>
                @foreach (var Item in ViewBag.ClientList)
                {
                    <tr>
                        <td>
                            <div >
                                <input asp-for="ClientId" id="@Item.Id" type="radio" name="ClientItem" value="@Item.Id" />
                                <label for="@Item.Id"  style="font-weight: 400">@Item.CompanyName</label>
                            </div>
                        </td>
                        <td>
                            <label for="@Item.Id"  style="font-weight: 400">
                                @Item.City
                            </label>
                        </td>
                        <td>
                            <label for="@Item.Id"  style="font-weight: 400">
                                <span ></span>
                                @Item.Country.CountryName
                            </label>
                        </td>
                    </tr>

 - 

}
                </tbody>
            </table>
        </div>
    </div>
</fieldset>

Here are the Booking, Client, and Country Models:

public class Bookings
{
    public int Id { get; set; }
    public int Type { get; set; }

    [Display(Name = "Client")]
    public int ClientId { get; set; }
    public virtual Client? Client { get; set; }

    //Truncated

    // . . .
}

public class Client
{
    public int Id { get; set; }

    [Display(Name = "Company Name")]
    public string CompanyName { get; set; }

    // Address
    [Display(Name = "Address")]
    public string Address1 { get; set; }
    [Display(Name = " ")]
    public string? Address2 { get; set; }
    public string City { get; set; }
    public string County { get; set; }

    [Display(Name = "Country")]
    public int CountryID { get; set; }
    public virtual Country? Country { get; set; }

    [Display(Name = "Postal Code")]
    public string PostalCode { get; set; }

    //Truncated

    // . . .   
}

public class Country
{
    public int Id { get; set; }
    public string CountryName { get; set; }
    public string CountryCode { get; set; }
}

What is confusing me here is how simply adding ViewData["CountryList"] to the controller code causes the Client's Country Object/Property to be populated with the correct data. If I remove the line of code, this Country object is null and requesting the page results in an error because I'm asking for information from a null object. This I expect.

I inserted a breakpoint on return View(); to be able to inspect the values. Note how the CountryId has its int value as stored in the database, but without the ViewData["CountryList"] line, the Country object is null. For clarity, Country ID 234 in my dataset is the United Kingdom, matching the country shown in the first screenshot.

With ViewData["CountryList"] With ViewData

Without ViewData["CountryList"] Without ViewData

How is the correct country data populated? I can't see how I refer to ViewData["CountryList"] in my View or any other way the Country property within the Client is populated using this ViewData member. Are there some inner workings within MVC that handle this for me? Is it something that shouldn't work yet somehow does work?

CodePudding user response:

You should read up on Entity Framework's documentation. Specifically on querying related data, it says:

Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.

If you want to have more implicit control over what you want to load, you can use Include. Something like:

ViewData["ClientList"] = _context.Clients
                                 .Include(client => client.Country)
                                 .ToList<Client>();

Should do the tricks.

CodePudding user response:

Are there some inner workings within MVC that handle this for me?

@Wiktor Zychla 's comment is right, you can read Read related data - ASP.NET MVC with EF Core to know more.

When the entity is first read, related data isn't retrieved. However, the first time you attempt to access a navigation property, the data required for that navigation property is automatically retrieved. A query is sent to the database each time you try to get data from a navigation property for the first time.

How is the correct country data populated?

If you use With ViewData["CountryList"], you can get the result. If you use Without ViewData["CountryList"], avoiding Country object is null, you could use Include. These are two ways to get the result.

  • Related