Creating an anonymous object via LINQ from IObservable? - c#

I've inherited a byzantine API that accesses remote data via JSON, the queries themselves are dealt with via TPL which for reasons I won't go into is backing me into a corner.
So, I have an IObservable subscription, which I query as thus;
mcSub.Take(1).Subscribe(x => x.Markets.ForEach(i => i.Prices.AvailableToBuy.ForEach(t => tabPanel.textBox1.AppendText(i.Id + " Back \t" + t.Size + " # " + t.Price))));
Obviously Markets and AvailableToBuy are lists, my question is - how can I create a LINQ query/object to extract i.Id, t.Size and t.Price?

I believe you can do it like this, by flattening twice (once the markets, and then the prices) in one collection with all prices:
//get all prices available to buy:
var pricesAvailableToBuy = mcSub.SelectMany(x => x.Markets)
.SelectMany(y => y.Prices.AvailableToBuy)
.Select(p => new { p.Id, p.Size, p.Price });
This gives you all prices for all markets in mcSub, while your initial statement works only on one market item (which also depends how it's called - if it's on a consumer worker task/thread then it makes sense).
Or, a similar formulation:
var pricesAvailableToBuy = mcSub.SelectMany(x => x.Markets.SelectMany(y => y.Prices.AvailableToBuy))
.Select(p => new { p.Id, p.Size, p.Price });

Woof.
That is one nasty honking subscribe/query blob. Let's see if we can clean that up somewhat...I see you're taking 1 from mcSub and subscribing to it, so I'll assume it's an enumerable of observables?
Let's first fake up some types I can refer to (I'll do my best to infer from your sample):
First, the data items:
public class Thing
{
public List<Market> Markets {get; set;}
}
public class Market
{
public Price Prices {get; set;}
}
public class Price
{
public List<AvailablePrice> AvailableToBuy {get; set;}
}
public class AvailablePrice
{
public string Id {get; set;}
public int Size {get; set;}
public decimal Price {get;set;}
}
Next, the thing that will generate an IEnumerable<IObservable<Thing>>, which I will ham-handedly hack together:
public IEnumerable<IObservable<Thing>> GetThingsObs()
{
var rnd = new Random();
return Enumerable.Range(0, 3).Select(_ => {
return Observable.Create<Thing>(obs =>
{
var things = Enumerable.Range(0, 3).Select(i => new Thing()
{
Markets = Enumerable.Range(0, 3).Select<int, Market>(x =>
{
return new Market()
{
Prices = new Price
{
AvailableToBuy = Enumerable.Range(0, 3)
.Select(y => new AvailablePrice { Id = string.Format("{0}:{1}:{2}", i, x, y), Size = rnd.Next(0, 10), Price = rnd.Next(0, 20) })
.ToList()
}
};
}).ToList()
});
foreach(var thing in things)
obs.OnNext(thing);
// this bit is important, but I'll get back to it later
obs.OnCompleted();
return Disposable.Empty;
});
});
}
Ok, now that we've got something that'll (I hope) map somewhat to your data, let's query!
IF the observable is known to complete (the bit above I glossed over), you can transform an IObservable<T> into an IEnumerable<T> reasonably safely - If it does not complete, this will basically hang, so be careful!
var mcSub = GetThingsObs();
var query =
// for each observable
from obs in mcSub
// this replaces your subscribe calls, but the observable MUST complete
// for this to return properly!
from thing in obs.ToEnumerable()
// After that, it's just multiple SelectManys
// (which is what the nested 'from x in y' calls get translated into)
// For each market
from market in thing.Markets
// for each price thingy
from price in market.Prices.AvailableToBuy
// select out your stuff
select new { price.Id, price.Size, price.Price };
That's the best guess I can make from the sample you've provided - if you can provide any more information/details, I'll take another shot at it if this doesn't work for ya.

Related

Distinct of Enumerable.Select of nested List in C#

public class Country
{
public List<State> States { get; set; } = new List<State>();
}
public class State
{
public List<City> Cities { get; set; } = new List<City>();
}
public class City
{
public decimal IdSeaLevel { get; set; }
}
IdSeaLevel Has these possible expected values: 0, 1, 2.
Then is needed to check al values inserted by user to prevent some different value.
Suppose that the user send us an country (object of Country class) with its list filled (and nested too).
How to get all Distinct IdSeaLevel inserted value by the user?
I was thinking like:
List<decimal> AllowedIdSeaLevellist = new List<decimal>(new decimal[] { 0, 1, 2 });
Now, I get a Distict inserted Values
HashSet<decimal> SentIdSeaLevelSet = country.States
.Select(s => s.Cities.IdSeaLevel).ToHashSet();
Check
bool badRequest= SentIdSeaLevelSet
.Where(s => AllowedIdSeaLevellist.All(a => a != s)).Any();
.SelectMany will map List of lists into single list (flattened)
var allSeaLevels = country.States
.SelectMany(s => s.Cities)
.Select(city => city.SeaLevelId)
.ToHashSet();
To get "invalid" sea levels you can alternatively to gather them while looping through sealevels.
var validSeaLevels = new[] { 0, 1, 2 }.ToHashSet();
var invalidSeaLevels = country.States
.SelectMany(s => s.Cities)
.Select(city => city.SeaLevelId)
.Where(level => validSeaLevels.Contains(level) == false)
.ToArray();
if (invalidSeaLevels.Any())
{
return BadRequest(invalidSeaLevels);
}
This type of deep linking is where SelectMany<T> becomes helpful:
HashSet<decimal> SentIdSeaLevelSet = country.States
.SelectMany(s => s.Cities.Select(c => c.IdSeaLevel)).Distinct().ToHashSet()
We want to project the IdSeaLevel but Cities is a List, so at some point you need the inner Cities.Select() but that can be inside or after the SelectMany which effectively flattens the hierarchy so that all of the nested Cities become a single list, the following would also work:
HashSet<decimal> SentIdSeaLevelSet = country.States
.SelectMany(s => s.Cities).Select(c => c.IdSeaLevel).Distinct().ToHashSet()
I prefer to use projection first inside the SelectMany it we never need any other properties from the City objects (the first example) but different applications and structures might dictate that the second expression performs better.
For the final comparison your logic looks ok, another way to compare lists is by using except:
bool badRequest= SentIdSeaLevelSet
.Except(AllowedIdSeaLevellist).Any();
This is functionally equivalent to your previous comparison and works because the types of the collections being compared are the same, runtime might be marginally faster but at this level you base your decision on code readability, which is a subjective topic on its own, but I prefer the except version specifically when we are comparing lists.

How to use Group By with Task<IEnumerable<Entity>> in LINQ

I am new to the entity framework and LINQ. Trying to learn it by example.
I have an Entity called "Participant" as below:
public class Participant
{
public int Id { get; set; }
public int Count { get; set; }
public string Zip { get; set; }
public string ProjectStatus { get; set; }
public string IncomingSource { get; set; }
}
I am trying to use Group by and return the result as Task<IEnumerable<Participant>>. The Sql Query that I found the is :
SELECT Count(Id) as #, Zip FROM [database].[dbo].[Participants] GROUP BY Zip Order By Zip
The Code that I am trying to accomplish the same result is like below:
public Task<IEnumerable<Participant>> GetResults()
{
var results = context.Participants
.GroupBy(i => i)
.Select(i => new {
Count = i.Key,
Zip = i.Count()
}
).ToList();
return results;
}
However, this gives me a conversion issue. The complete error stack is:
Cannot implicitly convert type 'System.Collections.Generic.List<<anonymous type: project.API.Models.Participant Count, int Zip>>' to 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<project.API.Models.Participant>>'
I am not sure how to solve convert these. Any help would be appreciated.
When you use GroupBy, you want to make groups of elements that have something in common. The property that you want to be in common is specified in parameter keySelector.
With this parameter you say: please make groups of Paraticipants, all with the same value of the property specified in the keySelector.
In your case: you want to make groups of Participants that have the same value of ZIP. In other words: if you fetch two Participants that are in the same group, you want to be certain that they have the same value for ZIP.
So first, change your keySelector:
var result = dbContext.Participants.GroupBy(participant => participant.Zip)
The result is a sequence of Groups. Every Group has a Key, and every Group IS (not has!) a sequence of Participants. All Participants have a value for property Zip that equals the value of Key.
After that, you want to take every group, and from every group you want to make a new Participant object, that has only two properties filled
Count is the number of Participants in the Group
Zip is the Zip of any of the elements in the Group, which is, as we saw earlier the Key of the Group.
.Select(groupOfParticipantsWithSameKey => new Participant
{
Count = groupOfParticipantsWithSameKey.Count(),
Zip = groupOfParticipantsWithSameKey.Key,
});
Did you notice that I changed the identifier i with a proper identifier. Choosing the proper identifier will help you identifying problems in LINQ. It might be a little more tying, but it helps you to understand what each element of the sequence your are processing represents.
By the way, there is an overload of Enumerable.GroupBy, the one with a parameter resultSelector. This makes your Select unnecessary.
var result = context.Participants
.GroupBy(participanti => participant.Zip,
// parameter resultSelector, take each common Zip, and all Participants that have this Zip
// to make one new object
(zip, participantsWithThisZip) => new Participant
{
Zip = zip,
Count = participantsWithThisZip.Count(),
});
This one is even easier to understand, because you have eliminated the identifier Key.
A small design improvement
You have created a method, that takes all Participants and returns one Participant per used Zip, where the Count is the number of Participants that have this Zip.
If you will be using this more often, then it would be a good idea to create a separate method for this, an extension method of IQueryable<Participant>. This way you can reuse the method with every sequence of Participants, not only all Participants within the database. See Extension Methods demystified
public static class ParticpantExtensions
{
public static IQueryable<Participant> ToParticipantsWithSameZip(
this IEnumerable<Participant> participants)
{
return participants.GroupBy(
participanti => participant.Zip,
(zip, participantsWithThisZip) => new Participant
{
Zip = zip,
Count = participantsWithThisZip.Count(),
});
}
}
Usage:
Your original method:
Task<IList<Participant>> FetchParticipantsWithSameZipAsync()
{
using (var dbContext in new MyDbContext(...))
{
return await dbContext.ToParticipantsWithSameZip().ToListAsync();
}
}
You can reuse it in the non-async version:
IList<Participant>> FetchParticipantsWithSameZipAsync()
{
using (var dbContext in new MyDbContext(...))
{
return dbContext.ToParticipantsWithSameZip().ToList();
}
}
But now you can also intertwine it with other LINQ methods:
var newYorkParticipantsWithSameZip = dbContext.Participants
.Where(participant => participant.State == "New York")
.ToParticipantsWithSameZip()
.OrderBy(participant => participant.Count())
.ToList();
Several advantages:
Reusable
Code looks cleaner,
Easier to understand what it does
You can unit test it without a database: any IQueryable<Participant> will do.
If you need to change ToParticipantsWithSameZip, there is only one place that you have to change and to rewrite the test.
So if you will be using it on several places: consider the extension method
The 2 easiest approaches would be either remove Task from the method signature making the method synchronous
public IEnumerable<Participant> GetResults()
Or if you wanted the method to use the async and await pattern use the async keyword in the method signature and call await and ToListAsync()
public async Task<IEnumerable<Participant>> GetResults()
{
var results = await context.Participants
.GroupBy(i => i)
.Select(i => new {
Count = i.Key,
Zip = i.Count()
}
).ToListAsync();
Note : in such case, you would likely want to rename the method GetResultsAsync
As TheGeneral mentioned.. use async and rename method:
public async Task<IEnumerable<Participant>> GetResultsAsync()
{
return context.Participants
.GroupBy(i => i.Zip)
.Select(i => new Participant
{
Count = i.Count(),
Zip = i.Zip
}
).ToListAsync();
}
Select is like What do u want to extract from ur particular query.
here u are creating an anonymous type by using new {...}
.Select(i => new {
Count = i.Key,
Zip = i.Count()
}
thats why its producing
List<anonymous>' this type.
but U want List so what to do ? anonymous type return Participant.
like this
.Select(i => new Participant
{
Count = i.Count(),
Zip = i.Zip
}

More Elegant LINQ Alternative to Foreach Extension

This is purely to improve my skill. My solution works fine for the primary task, but it's not "neat". I'm currently working on a .NET MVC with Entity framework project. I know only basic singular LINQ functions which have sufficed over the years. Now I'd like to learn how to fancy.
So I have two models
public class Server
{
[Key]
public int Id { get; set; }
public string InstanceCode { get; set; }
public string ServerName { get; set; }
}
public class Users
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int ServerId { get; set; } //foreign key relationship
}
In one of my view models I was asked to provide a dropdown list for selecting a server when creating a new user. The drop down list populated with text and value Id as an IEnumerable
Here's my original property for dropdown list of servers
public IEnumerable<SelectListItem> ServerItems
{
get { Servers.ToList().Select(s => new selectListItem { Value = x.Id.ToString(), Text = $"{s.InstanceCode}#{s.ServerName}" }); }
}
Update on requirements, now I need to display how many users are related to each server selection. Ok no problem. Here's what I wrote off the top of my head.
public IEnumerable<SelectListItem> ServerItems
{
get
{
var items = new List<SelectListItem>();
Servers.ToList().ForEach(x => {
var count = Users.ToList().Where(t => t.ServerId == x.Id).Count();
items.Add(new SelectListItem { Value = x.Id.ToString(), Text = $"{x.InstanceCode}#{x.ServerName} ({count} users on)" });
});
return items;
}
}
This gets my result lets say "localhost#rvrmt1u (8 Users)" but thats it..
What if I want to sort this dropdown list by user count. All I'm doing is another variable in the string.
TLDR ... I'm sure that someone somewhere can teach me a thing or two about converting this to a LINQ Query and making it look nicer. Also bonus points for knowing how I could sort the list to show servers with the most users on it first.
OK, we have this mess:
var items = new List<SelectListItem>();
Servers.ToList().ForEach(x => {
var count = Users.ToList().Where(t => t.ServerId == x.Id).Count();
items.Add(new SelectListItem { Value = x.Id.ToString(), Text = $"{x.InstanceCode}#{x.ServerName} ({count} users on)" });
});
return items;
Make a series of small, careful, obviously-correct refactorings that gradually improve the code.
Start with: Let's abstract those complicated operations to their own methods.
Note that I've replaced the unhelpful x with the helpful server.
int UserCount(Server server) =>
Users.ToList().Where(t => t.ServerId == server.Id).Count();
Why on earth is there a ToList on Users? That looks wrong.
int UserCount(Server server) =>
Users.Where(t => t.ServerId == server.Id).Count();
We notice that there is a built-in method that does these two operations together:
int UserCount(Server server) =>
Users.Count(t => t.ServerId == server.Id);
And similarly for creating an item:
SelectListItem CreateItem(Server server, int count) =>
new SelectListItem
{
Value = server.Id.ToString(),
Text = $"{server.InstanceCode}#{server.ServerName} ({count} users on)"
};
And now our property body is:
var items = new List<SelectListItem>();
Servers.ToList().ForEach(server =>
{
var count = UserCount(server);
items.Add(CreateItem(server, count);
});
return items;
Already much nicer.
Never use ForEach as a method if you're just going to pass a lambda body! There's already a built-in mechanism in the language that does it better! There is no reason to write items.Foreach(item => {...}); when you could simply write foreach(var item in items) { ... }. It's simpler and easier to understand and debug, and the compiler can optimize it better.
var items = new List<SelectListItem>();
foreach (var server in Servers.ToList())
{
var count = UserCount(server);
items.Add(CreateItem(server, count);
}
return items;
Much nicer.
Why is there a ToList on Servers? Completely unnecessary!
var items = new List<SelectListItem>();
foreach(var server in Servers)
{
var count = UserCount(server);
items.Add(CreateItem(server, count);
}
return items;
Getting better. We can eliminate the unnecessary variable.
var items = new List<SelectListItem>();
foreach(var server in Servers)
items.Add(CreateItem(server, UserCount(server));
return items;
Hmm. This gives us an insight that CreateItem could be doing the count itself. Let's rewrite it.
SelectListItem CreateItem(Server server) =>
new SelectListItem
{
Value = server.Id.ToString(),
Text = $"{server.InstanceCode}#{server.ServerName} ({UserCount(server)} users on)"
};
Now our prop body is
var items = new List<SelectListItem>();
foreach(var server in Servers)
items.Add(CreateItem(server);
return items;
And this should look familiar. We have re-invented Select and ToList:
var items = Servers.Select(server => CreateItem(server)).ToList();
Now we notice that the lambda can be replaced with the method group:
var items = Servers.Select(CreateItem).ToList();
And we have reduced that whole mess to a single line that clearly and unambiguously looks like what it does. What does it do? It creates an item for every server and puts them in a list. The code should read like what it does, not how it does it.
Study the techniques I used here carefully.
Extract complex code to helper methods
Replace ForEach with real loops
Eliminate unnecessary ToLists
Revisit earlier decisions when you realize there's an improvement to be made
Recognize when you are re-implementing simple helper methods
Don't stop with one improvement! Each improvement makes it possible to do another.
What if I want to sort this dropdown list by user count?
Then sort it by user count! We abstracted that away into a helper method, so we can use it:
var items = Servers
.OrderBy(UserCount)
.Select(CreateItem)
.ToList();
We now notice that we're calling UserCount twice. Do we care? Maybe. It could be a perf problem to call it twice, or, horrors, it might not be idempotent! If either are a problem then we need to undo a decision we made before. It's easier to deal with this situation in comprehension mode rather than fluent mode, so let's rewrite as a comprehension:
var query = from server in Servers
orderby UserCount(server)
select CreateItem(server);
var items = query.ToList();
Now we go back to our earlier:
SelectListItem CreateItem(Server server, int count) => ...
and now we can say
var query = from server in Servers
let count = UserCount(server)
orderby count
select CreateItem(server, count);
var items = query.ToList();
and we are only calling UserCount once per server.
Why go back to comprehension mode? Because to do this in fluent mode makes a mess:
var query = Servers
.Select(server => new { server, count = UserCount(server) })
.OrderBy(pair => pair.count)
.Select(pair => CreateItem(pair.server, pair.count))
.ToList();
And it looks a little ugly. (In C# 7 you could use a tuple instead of an anonymous type, but the idea is the same.)
The trick with LINQ is just to type return and go from there. Don't create a list and add items to it; there is usually a way to select it all in one go.
public IEnumerable<SelectListItem> ServerItems
{
get
{
return Servers.Select
(
server =>
new
{
Server = server,
UserCount = Users.Count( u => u.ServerId = server.Id )
}
)
.Select
(
item =>
new SelectListItem
{
Value = item.Server.Id.ToString(),
Text = string.Format
(
#"{0}{1} ({2} users on)" ,
item.Server.InstanceCode,
item.Server.ServerName,
item.UserCount
)
}
);
}
}
In this example there are actually two Select statements-- one to extract the data, and one to do the formatting. In an ideal situation the logic for those two tasks would be separated into different layers, but this is an OK compromise.

Linq sum on object w/o group

This seems simple enough but I'm not getting it for some reason.
Given:
public class Foo
{
public List<Bar> Bars { get; private set; }
public Bar Totals { get; private set; }
public Foo()
{
// Blah blah something to populate List of Bars
this.Bars = new List<Bar>()
{
new Bar("Some dude", 50, 1),
new Bar("Some other dude", 60,25)
};
// Calculate Total
var totals = Bars
.GroupBy(gb => gb.CustomerName) // When I comment out line i get "Bar does not contain a definition for "Sum" and no extension...." I want the sum of this without the group.
.Select(s => new
{
Cost = s.Sum(x => x.Cost),
Donation = s.Sum(x => x.Donation),
}
).ToList();
Totals = new Bar("Totals", totals[0].Cost, totals[0].Donation);
}
}
public class Bar
{
public string CustomerName { get; private set; }
public int Cost { get; private set; }
public int Donation { get; private set; }
public int Total { get { return Cost + Donation; } }
public Bar(string customerName, int cost, int donation)
{
this.CustomerName = customerName;
this.Cost = cost;
this.Donation = donation;
}
}
I'm having a few problems here:
-This works with a group by, but if i take out the group by which is my end goal I get "Bar does not contain a definition for "Sum" and no extension....". I want this sum on the entire collection, so do not want a group by.
-I'm creating an anon object before placing into a Bar because I'm not sure how to create a Bar without a parameterless constructor (and I can't add one to this particular class)
-I don't like accessing the "var totals" data using index 0 - should I not be ToListing at the end? If not, how do i access the properties? totals.Cost does not work.
Please help me figure out the proper way to get around my issue (specifically the 3 bullet points above this paragraph). Pretty new to the fluent (and linq in general)syntax and I'm trying to figure out the right way to do it.
EDIT:
thanks for the responses all. Taking kind of a combination of several answers really got me to what my end goal was (but biggest thanks D Stanley)
This is how I'm implementing now:
public Foo()
{
// ....
// Calculate Total
Totals = new Bar("Totals", Bars.Sum(s => s.Cost), Bars.Sum(s => s.Donation));
}
Guess I was just making it more complicated than it needed to be! :O
The s variable in the lambda is of type Bar if you remove the GroupBy. You want it to be List<Bar> instead in your case. So, what I think you want is something like:
var totalCosts = Bars.Sum(x => x.Cost);
var totalDonations = Bars.Sum(x => x.Donation);
but if i take out the group by I get "Bar does not contain a definition for "Sum"
That's because when you take out the GroupBy you're iterating over the individual items instead of a collection of groups. If you want to sum the entire collection use
var totals = new
{
Cost = Bars.Sum(x => x.Cost),
Donation = Bars.Sum(x => x.Donation),
}
;
or if you want a collection with one item, just change your GroupBy:
var totals = Bars
.GroupBy(gb => true) // trivial grouping
.Select(s => new Bar
{
Cost = s.Sum(x => x.Cost),
Donation = s.Sum(x => x.Donation),
}
).ToList();
-I'm casting to an anon object before placing into a Bar because I'm not sure how to cast it in without a parameterless constructor (and I can't add one to this particular class)
Just change your projection to
var totals = new Bar("Totals", Bars.Sum(x => x.Cost), Bars.Sum(x => x.Donation));
I don't like accessing the "var totals" data using index 0 - should I not be ToListing at the end? If not, how do i access the properties? totals.Cost does not work.
If you take out the group by you end up with just one object. If you have a colection with one item you could use First:
Totals = new Bar("Totals", totals.First().Cost, totals.First().Donation);
I want this sum on the entire collection, so do not want a group by.
Then use Sum on the collection
Bars.Sum(x => x.Cost)
I'm casting to an anon object before placing into a Bar because I'm not sure how to cast it in without a parameterless constructor (and I can't add one to this particular class)
You are not casting, you are creating anonymous objects
I don't like accessing the "var totals" data using index 0 - should I not be ToListing at the end? If not, how do i access the properties? totals.Cost does not work.
If you want single result use First.
It's simple enough, when you do Bars.Select(s =>, s is of type Bar and Bar has no definition of Sum. If you want the sum of all of it without any grouping, you can do:
Bars.Sum(b => b.Cost);
Bars.Sum(b => b.Donation);
You only need this :
Totals = new Bar("Totals", Bars.Sum(o => o.Cost), Bars.Sum(o => o.Donation));

Average extension method in Linq for default value

Anyone know how I can set a default value for an average? I have a line like this...
dbPlugins = (from p in dbPlugins
select new { Plugin = p, AvgScore = p.DbVersions.Average(x => x.DbRatings.Average(y => y.Score)) })
.OrderByDescending(x => x.AvgScore)
.Select(x => x.Plugin).ToList();
which throws an error becase I have no ratings yet. If I have none I want the average to default to 0. I was thinking this should be an extension method where I could specify what the default value should be.
There is: DefaultIfEmpty.
I 'm not sure about what your DbVersions and DbRatings are and which collection exactly has zero items, but this is the idea:
var emptyCollection = new List<int>();
var average = emptyCollection.DefaultIfEmpty(0).Average();
Update: (repeating what's said in the comments below to increase visibility)
If you find yourself needing to use DefaultIfEmpty on a collection of class type, remember that you can change the LINQ query to project before aggregating. For example:
class Item
{
public int Value { get; set; }
}
var list = new List<Item>();
var avg = list.Average(item => item.Value);
If you don't want to/can not construct a default Item with Value equal to 0, you can project to a collection of ints first and then supply a default:
var avg = list.Select(item => item.Value).DefaultIfEmpty(0).Average();
My advice would to create a reusable solution instead of a solution for this problem only.
Make an extension method AverageOrDefault, similar to FirstOrDefault. See extension methods demystified
public static class MyEnumerableExtensions
{
public static double AverageOrDefault(this IEnumerable<int> source)
{
// TODO: decide what to do if source equals null: exception or return default?
if (source.Any())
return source.Average();
else
return default(int);
}
}
There are 9 overloads of Enumerable.Average, so you'll need to create an AverageOrDefault for double, int?, decimal, etc. They all look similar.
Usage:
// Get the average order total or default per customer
var averageOrderTotalPerCustomer = myDbContext.Customers
.GroupJoin(myDbContext.Orders,
customer => customer.Id,
order => order.CustomerId,
(customer, ordersOfThisCustomer) => new
{
Id = customer.Id,
Name = customer.Name,
AverageOrder = ordersOfThisCustomer.AverageOrDefault(),
});
I don't think there's a way to select default, but how about this query
dbPlugins = (from p in dbPlugins
select new {
Plugin = p, AvgScore =
p.DbVersions.Any(x => x.DbRatings) ?
p.DbVersions.Average(x => x.DbRatings.Average(y => y.Score)) : 0 })
.OrderByDescending(x => x.AvgScore)
.Select(x => x.Plugin).ToList();
Essentially the same as yours, but we first ask if there are any ratings before averaging them. If not, we return 0.

Categories

Resources