-4

I am trying to create a LINQ query that allows the user to select Either Min Or Max from a drop down that I have on my web page. If the user selects Min it will display the record with the lowest Quote (Quote coming from a database table called rentals)

I have not had any luck with .Min() and .Max(). I am not sure how to go about this so any help would be appreciated.

This is my if statement for deciding what option the user has chosen on the drop down box:

namespace CarGarageSales.Pages.Queries
{
    [BindProperties]
    public class Query3Model : PageModel
    {
        private readonly CarGarageSales.Data.CarGarageSalesContext _context;

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

        [BindProperty(SupportsGet = true)]

        public int UserInput { get; set; }

        public Query3Model(CarGarageSales.Data.CarGarageSalesContext context)
        {
            _context = context;
        }

        public async Task OnGetAsync()
        {

            if (UserInput == 0)
            {
                var Rentals = (from s in _context.Rentals
                               select s);

                Rental = await Rentals.ToListAsync();
            }
            else if (UserInput == 1)
            {
                var Rentals = (from s in _context.Rentals
                               select s).Min();

            }
            else
            {
                var Rentals = (from s in _context.Rentals
                               select s.Quote).Max();
            }
        }
    }
}

This is my HTML section:

@page
@model CarGarageSales.Pages.Queries.Query3Model
@{
    ViewData["Title"] = "Query3";
}

<h2>Query3</h2>


<form>
    <p>
        Min or Max?:<select asp-for="UserInput">
       <option></option>    
       <option>Min</option>
       <option>Max</option>
    </select>

        <input type="submit" value="Search" />
    </p>
</form>


<p class="Text">Here is the record for the Rentals you requested!</p>

<table class="table Text">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Rental[0].RentalID)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Rental[0].Price)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Rental[0].Duration)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Rental[0].Quote)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Rental)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.RentalID)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Price)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Duration)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Quote)
                </td>
            </tr>
        }
    </tbody>
</table>

Rentals class:

namespace CarGarageSales.Models
{
    [BindProperties]
    public class Rental
    {
        [Required]
        public int RentalID { get; set; }

        [Required]
        [Display(Name = "Price")]
        [Range(100, 200000, ErrorMessage = "Price must be between 100 and 200,000 Pounds")]
        public decimal Price { get; set; }

        [Required]
        [Display(Name = "Duration")]
        [Range(1, 36,ErrorMessage = "Duration must be between 1 and 36 Months")]
        public int Duration { get; set; }

        [Required]
        [Display(Name = "Quote")]
        [Range(20, 10000, ErrorMessage = "Quote must be between 20 and 10000 Pounds")]
        public decimal Quote { get; set; }

        public Customer Customer { get; set; }

        public virtual IList<Car> Cars { get; set; }
        public virtual IList<SalesManRental> SalesManRental { get; set; }
    }
}
Darren_D19
  • 121
  • 9
  • 2
    Can you show what the Rentals class looks like? At minute you are trying to find the Max/Min of a class, which doesn't really have any meaning. You will need to take the Min/Max of one of its properties. – Sam Walpole Dec 13 '19 at 15:08
  • 1
    What is going wrong with the code you have so far? Are you getting any errors, or is it just not working as expected? – Matt U Dec 13 '19 at 15:08
  • also even if your linq query would be working you are storing returned value in local variable and does nothing with it – Selvin Dec 13 '19 at 15:09
  • You are trying to find the Min/Max of what? – rjs123431 Dec 13 '19 at 15:09
  • @SamWalpole I have edited it to add the rentals class – Darren_D19 Dec 13 '19 at 15:10
  • @MattU The data isn't changing depending on if the user has selected Min or Max – Darren_D19 Dec 13 '19 at 15:11
  • @Selvin Would you be able to show me how to do something with it to get it to work? – Darren_D19 Dec 13 '19 at 15:13
  • @rjs123431 I want to find the Min and Max quote from the rentals table in my database – Darren_D19 Dec 13 '19 at 15:15
  • This post has some good examples of how you could do it https://stackoverflow.com/questions/914109/how-to-use-linq-to-select-object-with-minimum-or-maximum-property-value Basically you need to get the min/max of the price property of Rentals, not the rentals object itself – Sam Walpole Dec 13 '19 at 15:30

5 Answers5

0

Let's see an example below (Here is your live code)

using System;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var option = 1;
        var list = new[]{ new Rental{ RentalID = 1,  Quote = 1 }, new Rental{ RentalID = 2,  Quote = 2 }, new Rental{ RentalID = 3,  Quote = 3 }, new Rental{ RentalID = 4,  Quote = 1 }, new Rental{ RentalID = 5,  Quote = 3 } };

        var minQuote = list.Min((p => p.Quote));        
        var maxQuote = list.Max((p => p.Quote));
        var result = list.Where(p => (option == 1 && p.Quote == minQuote) || (option == 2 && p.Quote == maxQuote));
        if(option == 0)
            result = list; 

        foreach(var item in result)
                Console.WriteLine(item.Quote);
    }


    public class Rental
    {
        public int RentalID { get; set; }

        public decimal Price { get; set; }

        public decimal Quote { get; set; }
    }
}

Note: Change value of option in range 0, 1, 2 to test your case.

Nguyễn Văn Phong
  • 13,506
  • 17
  • 39
  • 56
  • Thanks for taking the timet to create an example, but the code you posted just displays out all the quotes from smallest to largest, am I missing something? – Darren_D19 Dec 13 '19 at 15:32
  • Thanks, I am not sure how to go about converting this into a web application and the way I have listed in my post, would it be difficult? – Darren_D19 Dec 13 '19 at 15:41
0

You last example within the else should work to return the max Quote, as long as you are sure have data in _context.Rentals. I am not sure why you are using Async Task but you are not awaiting anything within UserInput == 1 or the else, only UserInput == 0

This should work

var Rentals = (from s in _context.Rentals select s.Quote).Max();

Example (test it here https://dotnetfiddle.net/2SnPcS)

using System;
using System.Collections.Generic;
using System.Linq;

public class Program {
    public class Rental
    {
        public int RentalID { get; set; }
        public decimal Price { get; set; }
        public int Duration { get; set; }
        public decimal Quote { get; set; }
    }

    public static void Main()
    {
        // sample data
        var rentals = new List<Rental> {
            new Rental { RentalID = 1, Price = 100.00m, Duration = 2, Quote = 200.00m },
            new Rental { RentalID = 2, Price = 100.00m, Duration = 2, Quote = 200.00m },
            new Rental { RentalID = 3, Price = 100.00m, Duration = 1, Quote = 100.00m },
            new Rental { RentalID = 4, Price = 100.00m, Duration = 1, Quote = 100.00m },
            new Rental { RentalID = 5, Price = 100.00m, Duration = 5, Quote = 500.00m }
        };

        // get min quote
        var minQuote = (from s in rentals select s.Quote).Min();

        // get max quote
        var maxQuote = (from s in rentals select s.Quote).Max();

        Console.WriteLine(string.Format("min={0} max={1}", minQuote, maxQuote));

        // Outputs: min=100.00 max=500.00
    }
}

I am not sure what the rest of your application looks like or how it is being used, but yes you can call your methods anything you want. Do you want just the Quote decimal value, or the entire Rental associated with it? To change your OnGetAsync method based on what you showed us, getting the decimal value only, it would look something like this (you were almost there).

public decimal GetQuoteAmount()
{
    // this could be min
    if (UserInput == 0)
    {
        return (from s in _context.Rentals select s.Quote).Min();
    }

    // this could be max
    else if (UserInput == 1)
    {
        return (from s in _context.Rentals select s.Quote).Max();
    }
    else 
    {
        // is there a third option? What do you want to happen if they do not select Min or Max?
        return 0;
    }
}
chrisbyte
  • 1,408
  • 3
  • 11
  • 18
  • Thanks for this! , I must not have any data in the _context.Rentals as it is not working, also am I able to call the method anything I want and it won't have an effect on my query? and also would this be easy to convert to a web application so that the user could select min or max from a drop down and then it output the min or max quote depending on the users choice? – Darren_D19 Dec 13 '19 at 15:40
  • I have edited my answer based on your comment, hopefully it can point you in the right direction. – chrisbyte Dec 13 '19 at 18:48
  • Thanks for the edit, I am getting an error "NullReferenceException: Object reference not set to an instance of an object." on the HTML file. Is there anyway I could give you access to the file and you could have a look? Thanks! – Darren_D19 Dec 13 '19 at 23:29
0

Not tested, but you could do something like this:

        var filteredRentals = _context.Rentals.GroupBy(g => g.RentalID)
            .Select(x => new Rental
            {
                RentalID = x.Key,
                Quote = x.Max(z => z.Quote),
                Duration = x.FirstOrDefault(r => r.RentalID == x.Key && r.Quote == x.Max(z => z.Quote)).Duration,
                Price = x.FirstOrDefault(r => r.RentalID == x.Key && r.Quote == x.Max(z => z.Quote)).Price
            }).ToList();

The idea is you group it by RentalID to get the Max/Min Quote, then get the first record that has those RentalID and Quote

In case they have same quote and you want to show all records instead of the first:

        var groupedRentals = _context.Rentals.GroupBy(g => g.RentalID)
            .Select(x => new
            {
                RentalID = x.Key,
                Quote = x.Max(z => z.Quote)
            }).ToList();

        var filteredGroupedRentals = _context.Rentals.Where(r => groupedRentals.Any(g =>
            g.RentalID == r.RentalID &&
            g.Quote == r.Quote))
            .ToList();
rjs123431
  • 688
  • 4
  • 14
0

As others have mentioned, you'll need to use .Select(s => s.Quote).Min() (or .Max). However, your select list options don't have any values. You need to specify the appropriate values, such as:

<option value="0">All</option>
<option value="1">Min</option>
<option value="2">Max</option>
Matt U
  • 4,970
  • 9
  • 28
0

I think part of the problem is that you only assign Rental in the case where UserInput == 0. Adding an assignment in the other cases should make your results update when the user changes their input.

Additionally, it sounds like you want to get the full records of all the objects with the largest (or smallest) Quote. If this is the case, then the OrderBy and GroupBy methods should help here because they allow you to sort the data based on it's properties, like the value of Quote, and group data with the same value together.

For example:

public async Task OnGetAsync()
{
    if (UserInput == 0)
    {
        Rental = _context.Rentals().ToListAsync();
    }
    else if (UserInput == 1)
    {
        // Get the group of items with min quote    
        Rental = _context.Rentals()
            .GroupBy(r => r.Quote) // Group items with the same quote (Quote becomes the Key)
            .OrderBy(g => g.Key)   // Order the groups by quote (smallest will be first)
            .FirstOrDefault()      // Choose the first group (those with min quote)
            .ToListAsync();        // Select the items into a list
    }
    else
    {
        // Get the group of items with max quote    
        Rental = _context.Rentals()
            .GroupBy(r => r.Quote) // Group items with the same quote (Quote becomes the Key)
            .OrderBy(g => g.Key)   // Order the groups by quote (smallest will be first)
            .LastOrDefault()       // Choose the last group (those with max quote)
            .ToListAsync();        // Select the items into a list
    }
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43