Click here to Skip to main content
15,887,952 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
In project i have two tables

- Restaurant (RestaurantId, Name, Adress)
- RestaurantReview (ReviewId, RestaurantId, Mark)
One restaurant can have several opinions. I would like my application to display restaurant and the average of its ratings. I create code:

var resReviews = (
  from x in _context.Restaurant
  join y in _context.Reviews on x.IdRestauracji equals y.RestaurantId into z
  from a in z
  group a by a.RestaurantId into g
  select new
  {

     RatingAverage = g.Average(x => Convert.ToInt32(x.Mark))
  }
).ToList();


In list are saved calculated average mark. I would also like to have restaurant information saved on the list. I try create something like this, but it doesn't work:

var resReviews = (
  from x in _context.Restaurant
  join y in _context.Reviews on x.IdRestauracji equals y.RestaurantId into z
  from a in z
  group a by a.RestaurantId into g
  select new
  {
     Restaurant = g.FirstOrDefault();
     RatingAverage = g.Average(x => Convert.ToInt32(x.Mark))
  }
).ToList();


What I have tried:

var lj = (from x in _context.Restaurant
                      join y in _context.Reviews on x.RestaurantId equals y.RestaurantId into z
                      select new
                      {
                          Name= x.Name,
                          Total = z.Average(x => Convert.ToInt32(x.Mark))
                      }).ToList();

RETURN Error:
System.InvalidOperationException: „Processing of the LINQ expression 'GroupJoin<Restaurant, Reviews, int, <>f__AnonymousType4<string, double>>(
    outer: DbSet<Restaurant>, 
    inner: DbSet<Reviews>, 
    outerKeySelector: (x) => x.RestaurantId, 
    innerKeySelector: (y) => y.RestaurantId, 
    resultSelector: (x, z) => new { 
        Name = x.Name, 
        Total = Average<Review>(
            source: z, 
            selector: (x) => ToInt32(x.Marks))
     })' by 'NavigationExpandingExpressionVisitor' failed. 
Posted
Updated 28-May-20 4:33am

Assuming you have suitable navigation properties defined, try something like this:
C#
var resReviews = _context.Restaurant.Select(x => new
{
    x.Name,
    RatingAverage = x.Reviews.Average(r => r.Mark)
}).ToList();
NB: You don't want to convert the Mark to an integer before calculating the average, since that will drag the average down. Using integers, the average of 1 and 2 will be 1, whereas the true average should be 1.5.
 
Share this answer
 
Comments
Maciej Los 27-May-20 16:09pm    
5ed!

Could I suggest that you do something like this? Group your Reviews colletion by RestaurantId as the key and use the Mark property for the collection that relates to that key. Output the result for each group as a value tuple consisting of the RestaurantId and the average value of the Mark collection. The Average method takes an enumerable of ints and returns a double so there is no need to convert the Mark property into a double.


C#
var reviewResults = Reviews.GroupBy(r => r.RestaurantId, r => r.Mark, (key, marks) => (Id:key,AverageMark: marks.Average()));

Then join the reviewResults to the Restaurants collection by RestaurantId to include any addional properties from the Restaurants collection that you require.


C#
var summaries= reviewResults.Join(Restaurants,  rev => rev.Id,  rest => rest.RestaurantId, (rev, rest) => (rest.Name,rev.AverageMark, rev.Id));

You can enumerate the summaries like this


C#
foreach(var(Name,Mark,Id)in summaries)
 {
   Console.WriteLine($"Restaurant {Name} Average Score {Mark}");
 }

There is no need for the summaries variable. I just put that in to simplify the explanation, you can join the two Linq statements together using the dot notation.

 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900