3

I'm following along in the book C# 8.0and .NET Core 3.0 - Modern Cross-Platform Development

I'm currently in Chapter 15 on exercise 15.2 where we are tasked with creating a Razor Page that produces a list of customers grouped by country. When you click on a customer name, it takes you to a new page showing the full contact details of that customer and a list of their orders.

Taking baby steps, I have built a page that has a card that will have the header as the country and then list each customer name on the card. However, my foreach loop is spitting out each data column under Customer.Country. So if there are 11 countries with Germany, it makes 11 cards with Germany as the title (see image).

It is also populating all of the customer's names as well under each country.

I found that I can use GroupBy() but as I explain below, that causes an invalid cast exception.

customers.cshtml

@page
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using NorthwindEntitiesLib
@model NorthwindWeb.Pages.CustomersModel
<div class="row">
    <h1 class="display2">Customers</h1>
</div>
<div class="row">
    @foreach (Customer customer in Model.Customers)
    {
        <div class="card border-info mb-3" style="max-width: 18rem">
            <div class="card-header text-white bg-info">
                @customer.Country
            </div>
            <ul class="list-group list-group-flush">
                @foreach (var name in Model.Customers)
                {
                    <li class="list-group-item">@name.ContactName</li>
                }
            </ul>
        </div>
    }
</div>

customers.cshtml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using NorthwindContextLib;
using NorthwindEntitiesLib;

namespace NorthwindWeb.Pages
{
    public class CustomersModel : PageModel
    {
        private Northwind db;

        public CustomersModel(Northwind injectedContext)
        {
            db = injectedContext;
        }

        public IEnumerable<Customer> Customers { get; set; }


        public void OnGet()
        {
            Customers = db.Customers
                .ToArray();

        }
    }
}

NorthwindEntitesLib

namespace NorthwindEntitiesLib
{
  public class Customer
  {
    public string CustomerID { get; set; }

    public string CompanyName { get; set; }

    public string ContactName { get; set; }

    public string ContactTitle { get; set; }

    public string Address { get; set; }

    public string City { get; set; }

    public string Region { get; set; }

    public string PostalCode { get; set; }

    public string Country { get; set; }

    public string Phone { get; set; }

    public string Fax { get; set; }

    public ICollection<Order> Orders { get; set; }
  }
}

I have tried using GroupBy to group the Countries and then populate the list but it gives an error for an invalid casting of type string.

@foreach (Customer customer in Model.Customers.GroupBy(c =>c.Country))
    {
        <div class="card border-info mb-3" style="max-width: 18rem">
            <div class="card-header text-white bg-info">
                @customer.Country
            </div>
            <ul class="list-group list-group-flush">
                @foreach (var name in Model.Customers)
                {
                    <li class="list-group-item">@name.ContactName</li>
                }
            </ul>
        </div>
    }

InvalidCastException: Unable to cast object of type 'System.Linq.Grouping`2[System.String,NorthwindEntitiesLib.Customer]' to type 'NorthwindEntitiesLib.Customer'.

4
  • Add Model.Customers.GroupBy(c =>c.Country).ToList() and see if that helps Commented May 6, 2020 at 19:04
  • Throws the same InvalidCastException as above. Commented May 6, 2020 at 19:39
  • I am working on an answer for you. one sec Commented May 6, 2020 at 19:41
  • I found that Model.Customers.GroupBy(c=>c.Country) .Select(c => c.First()) produced the result I wanted for the Grouping. Now I need to figure out how to get only those customers who live in that country under their prospective country. Commented May 6, 2020 at 19:54

2 Answers 2

3

Ok, so GroupBy gives you a Key property. That is what you will use for the country name.

So basically, you will use the following (warning untested)

@foreach (var country in Model.Customers.GroupBy(c =>c.Country))
    {
        <div class="card border-info mb-3" style="max-width: 18rem">
            <div class="card-header text-white bg-info">
                @country.Key
            </div>
            <ul class="list-group list-group-flush">
                @foreach (var customer in country.ToArray())
                {
                    <li class="list-group-item">@customer.ContactName</li>
                }
            </ul>
        </div>
    }
Sign up to request clarification or add additional context in comments.

2 Comments

That works and gets my CustomerName working right too! I must have tried it wrong earlier using the .key, which happens when you are learning! Thanks for your assistance @Patrick Mcvay
You were looping through the Model.Customers object twice. You needed to use the object you made with the GroupBy() method. I think that's where you were probably messing up, and no problem, happy to help.
-2

GroupBy is the command of System.Linq. You can't simply use it in random places without mentioning System.Linq.

1 Comment

I have it in my customers.cshtml.cs file, which is linked to my customers.cs file. It's part of .NET Core and EFCore

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.