Search code examples
c#linqentity-framework-coreasp.net-core-mvc

ASP.NET Core MVC: Passing one parameter from a view to a controller


I have my Regions Index here that passes the regionID for a region through as an asp-route-id that is meant to go to this URL when the link is clicked (which it does):

/Countries/Index/{id?}

@model IEnumerable<Assig1.Models.Region>

@{
    ViewData["Title"] = "Index";
}

<h1 class="alert-secondary p-2 text-center mb-3">Regions</h1>

<div class="row justify-content-start mt-2">

    <div class="card mx-auto col-lg-6 mb-4 text-center" style="width: 18rem;">
        <div class="card-body">
            <img src="https://geology.com/world/world-map.gif" class="card-img-top" alt="Map of the World">
            <h5 class="card-title text-center" style="position: relative; top: 15px;">All Regions</h5>
            <br>
            <a class="btn btn-success" asp-controller="Countries" asp-action="Index">Search</a> 
        </div>
    </div>
    

@foreach (var item in Model)
{
        <div class="card mx-auto col-lg-6 mb-4 text-center" style="width: 18rem;">
        <img src="@Html.DisplayFor(modelItem => item.ImageUrl)" class="card-img-top" alt="@Html.DisplayFor(modelItem => item.RegionName)">
        <div class="card-body">
            <h5 class="card-title text-center">@Html.DisplayFor(modelItem => item.RegionName)</h5>
            <br>
                <a class="btn btn-success" asp-controller="Countries" asp-action="Index" asp-route-id="@item.RegionId">Search</a> 
            </div>
        </div>
}
</div>

The Regions Model:

namespace Assig1.Models
{
    public partial class Region
    {
        public Region()
        {
            Countries = new HashSet<Country>();
        }

        public int RegionId { get; set; }
        public string RegionName { get; set; } = null!;
        public string? ImageUrl { get; set; }

        public virtual ICollection<Country> Countries { get; set; }
    }
}

And on the Countries Index Page, the page will change its behaviour if a region has been selected from the previous page.

  1. If no region has been selected, then the page will list all the country records in alphabetical order.
  2. If a region has been selected, then the page will list all countries associated with the selected region.
  3. This page must use a ViewModel.

Regardless of which mode, the countries listed will be in alphabetical order.

The Countries Index Page View:

@model Assig1.ViewModel.CountriesViewModel

@{
    ViewData["Title"] = "Index";
}

<h1 class="alert-secondary p-2 text-center mb-3">Countries</h1>

<p>
    <a class="btn btn-primary" asp-controller="Regions" asp-action="Index">Back to Regions</a>
</p>

<div class="row justify-content-start mt-2">

@foreach (var item in Model.CountryList) {
        <div class="card mx-auto col-lg-6 mb-4" style="width: 18rem;">
        <img src="@Html.DisplayFor(modelItem => item.ImageUrl)" class="card-img-top" alt="@Html.DisplayFor(modelItem => item.CountryName)>
        <div class="card-body">
            <br>
            <h5 class="card-title text-center">@Html.DisplayFor(modelItem => item.CountryName)</h5>
            <br>
            <a class="btn btn-primary" asp-controller="Countries" asp-action="Detail" asp-route-countryID="@item.CountryId">Details</a>
            <a class="btn btn-success" asp-controller="Cities" asp-action="Index" asp-route-countryID="@item.CountryId">Cities</a>
        </div>
}
</div>
</div>

My CountriesViewModel:

namespace Assig1.ViewModel
{
    public class CountriesViewModel
    {
        public int? CountryId { get; set; }

        public String CountryName { get; set; }

        public String ImageUrl { get; set; }

        public String RegionName { get; set; }

        public int RegionId { get; set; }

        public List<Models.Country> CountryList { get; set; }

    }
}

The Country Model:

namespace Assig1.Models
{
    public partial class Country
    {
        public Country()
        {
            Cities = new HashSet<City>();
            CountryEmissions = new HashSet<CountryEmission>();
            TemperatureData = new HashSet<TemperatureData>();
        }

        public int CountryId { get; set; }
        public string? Iso3 { get; set; }
        public string CountryName { get; set; } = null!;
        public int? RegionId { get; set; }
        public string? ImageUrl { get; set; }

        public virtual Region? Region { get; set; }
        public virtual ICollection<City> Cities { get; set; }
        public virtual ICollection<CountryEmission> CountryEmissions { get; set; }
        public virtual ICollection<TemperatureData> TemperatureData { get; set; }
    }
}

I am able to list all the country records in order when there is no region id passed through, but I can't get the page to list all the countries from a specific region when there is a region id passed through.

It keeps giving me the error:

Cannot Implicitly Convert Type 'System.Linq.IQueryable<Assign1.Models.Country> to 'Microsoft.EntityFrameworkCore.Query.IIncludableQueryable<Assign1.Models.Country, Assign1.Models.Region>

When I try to explicitly convert it, the page loads but as soon as I try to pass in the region id by clicking the link in the region index, it doesn't work. It still loads every single country.

My CountriesController:

namespace Assig1.Controllers
{
    public class CountriesController : Controller
    {
        private readonly EnvDataContext _context;

        public CountriesController(EnvDataContext context)
        {
            _context = context;
        }
        
        // GET: Countries
        public async Task<IActionResult> Index(CountriesViewModel vm, int? RegionId)
        {
            var envDataContext = _context.Countries.Include(c => c.Region);

            if (RegionId != null)
            {
                   envDataContext = envDataContext.Where(c => c.RegionId == RegionId);
            }
            
            vm.CountryList =
                await envDataContext.ToListAsync();

            return View(vm);
        }

It might be due to the Region index not using a ViewModel? Or I messed up something here clearly, I don't know.

The Domain Model for reference:

enter image description here

Thank you!


Solution

  • The provided error states that this is a compilation error with this line:

    var envDataContext = _context.Countries.Include(c => c.Region);
    

    envDataContext was declared as a Microsoft.EntityFrameworkCore.Query.IIncludableQueryable<Assign1.Models.Country, Assign1.Models.Region> type and you are trying the value with IQueryable<Country> in the next line.

    You can resolve it by casting it to IQueryable<Country> type during the declaration.

    var envDataContext = _context.Countries.Include(c => c.Region).AsQueryable();