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

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
}

Related

Flatten a sequence of sequences into a single sequence (List<string> from List<Object> contains that List<string>)

I'm trying to extract some Lists of strings into a single List.
First I have this class
public class Client
{
public string Name { get; set; }
public List<string> ApiScopes { get; set; }
}
Thus I'm getting my response as a List<Client> and my intention is to take all Client's Scopes into a single List without Looping
I've tried this by LINQ:
var x = clients.Select(c=> c.AllowedScopes.Select(x => x).ToList()).ToList();
this returns a List<List<string>>, and I just want to get all this into one Distinct list of strings.
It sounds like you want SelectMany (which flattens a sequence of sequences into a single sequence), with Distinct as well if you need a list with each scope only appearing once even if it's present for multiple clients:
var scopes = clients.SelectMany(c => c.ApiScopes).Distinct().ToList();
This assumes that Client.ApiScopes is never null. If it might be null, you need a little bit more work:
var scopes = clients
.SelectMany(c => ((IEnumerable<string>) c.ApiScopes) ?? Enumerable.Empty<string>())
.Distinct()
.ToList();
You can use SelectMany to flatten results :
var scopes=clients.SelectMany(client=>client.ApiScopes)
.Distinct()
.ToList();
This is equivalent to :
var scopes= ( from client in clients
from scope in client.ApiScopes
select scope
)
.Distinct()
.ToList();

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.

How to build () => new { x.prop} lambda expression dynamically?

How to dynamically create the below linq expression.
IQueryable abc = QueryData.Select(a => new { a, TempData = a.customer.Select(b => b.OtherAddress).ToList()[0] }).OrderBy(a => a.TempData).Select(a => a.a);
public class Orders
{
public long OrderID { get; set; }
public string CustomerID { get; set; }
public int EmployeeID { get; set; }
public double Freight { get; set; }
public string ShipCountry { get; set; }
public string ShipCity { get; set; }
public Customer[] customer {get; set;}
}
public class Customer
{
public string OtherAddress { get; set; }
public int CustNum { get; set; }
}
Actual data:
List<Orders> order = new List<Orders>();
Customer[] cs = { new Customer { CustNum = 5, OtherAddress = "Hello" }, new
Customer { CustNum = 986, OtherAddress = "Other" } };
Customer[] cso = { new Customer { OtherAddress = "T", CustNum = 5 }, new
Customer { CustNum = 777, OtherAddress = "other" } };
order.Add(new Orders(code + 1, "ALFKI", i + 0, 2.3 * i, "Mumbari", "Berlin", cs));
order.Add(new Orders(code + 2, "ANATR", i + 2, 3.3 * i, "Sydney", "Madrid", cso));
order.Add(new Orders(code + 3, "ANTON", i + 1, 4.3 * i, "NY", "Cholchester", cs));
order.Add(new Orders(code + 4, "BLONP", i + 3, 5.3 * i, "LA", "Marseille", cso));
order.Add(new Orders(code + 5, "BOLID", i + 4, 6.3 * i, "Cochin", "Tsawassen", cs));
public Orders(long OrderId, string CustomerId, int EmployeeId, double Freight, string ShipCountry, string ShipCity, Customer[] Customer = null)
{
this.OrderID = OrderId;
this.CustomerID = CustomerId;
this.EmployeeID = EmployeeId;
this.Freight = Freight;
this.ShipCountry = ShipCountry;
this.ShipCity = ShipCity;
this.customer = Customer;
}
If i sort the OtherAddress field 0th index means Customer field only sorted. I need to sort the whole order data based on OtherAddress field.
I have tried the below way:
private static IQueryable PerformComplexDataOperation<T>(this IQueryable<T> dataSource, string select)
{
string[] selectArr = select.Split('.');
ParameterExpression param = Expression.Parameter(typeof(T), "a");
Expression property = param;
for (int i = 0; i < selectArr.Length; i++)
{
int n;
if (int.TryParse(selectArr[i + 1], out n))
{
int index = Convert.ToInt16(selectArr[i + 1]);
property = Expression.PropertyOrField(Expression.ArrayIndex(Expression.PropertyOrField(property, selectArr[i]), Expression.Constant(index)), selectArr[i + 2]);
i = i + 2;
}
else property = Expression.PropertyOrField(property, selectArr[i]);
}
var TempData = dataSource.Select(Expression.Lambda<Func<T, object>>(property, param));
IQueryable<object> data = dataSource.Select(a => new { a, TempData = property});// Expression.Lambda<Func<T, object>>(property, param) });
return data;
}
Method call : PerformComplexDataOperation(datasource, "customer.0.OtherAddress")
I can get the value from this line : var TempData = dataSource.Select(Expression.Lambda>(property, param));
But i can't get the values in dataSource.Select(a => new { a, TempData = property});
It is working when we use the below code :
var TempData = dataSource.Select(Expression.Lambda<Func<T, object>>(property, param)).ToList();
IQueryable<object> data = dataSource.Select((a, i) => new { a, TempData = TempData[i] });
Is it proper solution ?
XY problem?
This feels like it's a case of the XY problem. Your solution is contrived (no offense intended), and the problem you're trying to solve is not apparent by observing your proposed solution.
However, I do think there is technical merit to your question when I read the intention of your code as opposed to your described intention.
Redundant steps
IQueryable abc = QueryData
.Select(a => new {
a,
TempData = a.customer.Select(b => b.OtherAddress).ToList()[0] })
.OrderBy(a => a.TempData)
.Select(a => a.a);
First of all, when you inline this into a single chained command, TempData becomes a redundant step. You could simply shift the first TempData logic (from the first Select) directly into the OrderBy lambda:
IQueryable abc = QueryData
.OrderBy(a => a.customer.Select(b => b.OtherAddress).ToList()[0])
.AsQueryable();
As you can see, this also means that you no longer need the second Select (since it existed only to undo the earlier Select)
Parametrization and method abstraction
You mentioned you're looking for a usage similar to:
PerformComplexDataOperation(datasource, "customer.0.OtherAddress")
However, this doesn't quite make sense, since you've defined an extension method:
private static IQueryable PerformComplexDataOperation<T>(this IQueryable<T> dataSource, string select)
I think you need to reconsider your intended usage, and also the method as it is currently defined.
Minor note, the return type of the method should be IQueryable<T> instead of IQueryable. Otherwise, you lose the generic type definition that LINQ tends to rely on.
Based on the method signature, your expected usage should be myData = myData.PerformComplexDataOperation("customer.0.OtherAddress").
Strings are easy hacks to allow you to circumvent an otherwise strongly typed system. While your strign usage is technically functional, it is non-idiomatic and it opens the door to unreadable and/or bad code.
Using strings leads to a contrived string parsing logic. Look at your method definition, and count how many lines are there simply to parse the string and translate that into actual code again.
Strings also mean that you get no Intellisense, which can cause unseen bugs further down the line.
So let's not use strings. Let's look back at how I initially rewrote the `OrderBy:
.OrderBy(a => a.customer.Select(b => b.OtherAddress).ToList()[0])
When you consider OrderBy as an ordinary method, no different from any custom method you and I can develop, then you should understand that a => a.customer.Select(b => b.OtherAddress).ToList()[0] is nothing more than a parameter that's being passed.
The type of this parameter is Func<A,B>, where:
A equals the type of your entity. So in this case, A is the same as T in your existing method.
B equals the type of your sorting value.
OrderBy(x => x.MyIntProp) means that B is of type int.
OrderBy(x => x.MyStringProp) means that B is of type string.
OrderBy(x => x.Customer) means that B is of type Customer.
Generally speaking, the type of B doesn't matter for you (since it will only be used by LINQ's internal ordering method).
Let's look at a very simple extension method that uses a parameter for its OrderBy:
public static IQueryable<A> OrderData<A, B>(this IQueryable<A> data, Func<A, B> orderbyClause)
{
return data
.OrderBy(orderbyClause)
.AsQueryable();
}
Using the method looks like:
IQueryable<MyEntity> myData = GetData(); //assume this returns a correct value
myData = myData.OrderData(x => x.MyIntProperty);
Notice how I did not need to specify either of the generic type arguments when calling the method.
A is already known to be MyEntity, because we're calling the method on an object of type IQueryable<MyEntity>.
B is already known to be an int, since the used lambda method returns a value of type int (from MyIntProperty)
As it stands, my example method is just a boring wrapper that does nothing different from the existing OrderBy method. But you can change the method's logic to suit your needs, and actually make it meaningfully different from the existing OrderBy method.
Your expectations
Your description of your goals makes me think that you're expecting too much.
I need to sort "customer.0.OtherAddress" nested file compared to whole base data. But it sorted only for that field. For this case, I find that field value and stored it to TempData. Then Sorting the TempData field.
i need to sort the parent nodes not an sibling alone. QueryData.Select(a => new { a, TempData = a.customer.Select(b => b.OtherAddress).ToList()[0] }).OrderBy(a => a.TempData).Select(a => a.a); I sorting a original data based on temp data. Then i split the original data alone.
It's not possible to sort an entire nested data structure based on a single OrderBy call. OrderBy only sorts the collection on which you call Orderby, nothing else.
If you have a list of Customer entities, who each have a list of Adress entities, then you are working with many lists (a list of customer and several lists of adresses). OrderBy will only sort the list that you ask it to sort, it will not look for any nested lists.
You mention that your TempData solution works. I actually wrote an entire answer contesting that notion (it should be functionally similar to my suggested alternatives, and it should always order the original list, not any nested list), until I noticed that you've made it work for a very insidious and non-obvious reason:
.Select(a => new {
a,
TempData = a.customer.Select(b => b.OtherAddress).ToList()[0]
})
You are calling .ToList(), which changes how the code behaves. You started off with an IQueryable<>, which means that LINQ was preparing an SQL command to retrieve the data when you enumerate it.
This is the goal of an IQueryable<>. Instead of pulling all the data into memory and then filtering it according to your specifications, it instead constructs a complex SQL query, and will then only need to execute a single (constructed) query.
The execution of that constructed query occurs when you try to access the data (obviously, the data needs to be fetched if you want to access it). A common method of doing so is by enumerating the IQueryable<> into an IEnumerable<>.
This is what you've done in the Select lambda. Instead of asking LINQ to enumerate your list of orders, you've asked it to enumerate every list of addresses from every customer from every order in the list of orders.
But in order to know which adresses need to be enumerated, LINQ must first know which customers it's supposed to get the adresses from. And to find out which customers it needs, it must first figure out which orders you're working with. The only way it can figure all of that out is by enumerating everything.
My initial suggestion, that you should avoid using the TempData solution, is still valid. It's a redundant step that serves no functional purpose. However, the enumeration that also takes place may actually be of use to you here, because it changes LINQ's behavior slightly. You claim that it fixes your problem, so I'm going to take your statement at face value and assume that the slightly different behavior between LINQ-to-SQL and LINQ-to-Entities solves your problem.
You can keep the enumeration and still omit the TempData workaround:
IQueryable abc = QueryData
.OrderBy(a => a.customer.Select(b => b.OtherAddress).ToList()[0])
.AsEnumerable()
.AsQueryable();
Some footnotes:
You can use ToList() instead of AsEnumerable(), the result is the same.
When you use First() or Single(), enumeration will inherently take place, so you don't need to call AsEnumerable() beforehand.
Notice that I cast the result to an IEnumerable<>, but then I immediately re-cast it to IQueryable<>. Once a collection has been enumerated, any further operation on it will occur in-memory. Casting it back to an IQueryable<> does not change the fact that the collection has already been enumerated.
But does it work?
Now, I think that this still doesn't sort all of your nested lists with a single call. However, you claim it does. If you still believe that it does, then you don't need to read on (because your problem is solved). Otherwise, the following may be useful to you.
SQL, and by extension LINQ, has made it possible to sort a list based on information that is not found in the list. This is essentially what you're doing, you're asking LINQ to sort a list of orders based on a related address (regardless of whether you want the adresses to be retrieved from the database or not!) You're not asking it to sort the customers, or the addresses. You're only asking it to sort the orders.
Your sort logic feels a bit dirty to me. You are supplying an Address entity to your OrderBy method, without specifiying any of its (value type) properties. But how are you expecting your addresses to be sorted? By alphabetical street name? By database id? ...
I would expect you to be more explicit about what you want, e.g. OrderBy(x => x.Address.Street).ThenBy(x => x.Address.HouseNumber) (this is a simplified example).
After enumeration, since all the (relevant) data is in-memory, you can start ordering all the nested lists. For example:
foreach(var order in myOrders)
{
order.Customer.Addresses = order.Customer.Addresses.OrderBy(x => x.Street).ToList();
}
This orders all the lists of addresses. It does not change the order of the list of orders.
Do keep in mind that if you want to order data in-memory, that you do in fact need the data to be present in-memory. If you never loaded the customer's addresses, you can't use addresses as a sorting argument.
Ordering the list of orders should be done before enumeration. It's generally faster to have it handled by your SQL database, which is what happens when you're working with LINQ-to-SQL.
Ordering nested lists should be done after enumeration, because the order of these lists is unrelated to the original IQueryable<Order>, which only focused on sorting the orders, not its nested related entities (during enumeration, the included entities such as Customer and Address are retrieved without ordering them).
You can transform your OrderBy so you don't need an anonymous type (though I like the Perl/Lisp Schwartzian Transform) and then it is straightforward to create dynamically (though I am not sure how dynamically you mean).
Using the new expression:
var abc = QueryData.OrderBy(a => a.customer[0].OtherAddress);
Not being sure what you mean by dynamic, you can create the lambda
x => x.OrderBy(a => a.customer[0].Otheraddress)
using Expression as follows:
var parmx = Expression.Parameter(QueryData.GetType(), "x");
var parma = Expression.Parameter(QueryData[0].GetType(), "a");
var abc2 = Expression.Lambda(Expression.Call(MyExtensions.GetMethodInfo((IEnumerable<Orders> x)=>x.OrderBy(a => a.customer[0].OtherAddress)),
new Expression[] { parmx,
Expression.Lambda(Expression.Property(Expression.ArrayIndex(Expression.Property(parma, "customer"), Expression.Constant(0)), "OtherAddress"), parma) }),
parmx);

How to modify only one or two field(s) in LINQ projections?

I have this LINQ query:
List<Customers> customers = customerManager.GetCustomers();
return customers.Select(i => new Customer {
FullName = i.FullName,
Birthday = i.Birthday,
Score = i.Score,
// Here, I've got more fields to fill
IsVip = DetermineVip(i.Score)
}).ToList();
In other words, I only want one or two fields of the list of the customers to be modified based on a condition, in my business method. I've got two ways to do this,
Using for...each loop, to loop over customers and modify that field (imperative approach)
Using LINQ projection (declarative approach)
Is there any technique to be used in LINQ query, to only modify one property in projection? For example, something like:
return customers.Select(i => new Customer {
result = i // telling LINQ to fill other properties as it is
IsVip = DetermineVip(i.Score) // then modifying this one property
}).ToList();
you can use
return customers.Select(i => {
i.IsVip = DetermineVip(i.Score);
return i;
}).ToList();
Contrary to other answers, you can modify the source content within linq by calling a method in the Select statement (note that this is not supported by EF although that shouldn't be a problem for you).
return customers.Select(customer =>
{
customer.FullName = "foo";
return customer;
});
You "can", if you create a copy constructor, which initializes a new object with the values of an existing object:
partial class Customer
{
public Customer(Customer original)
{
this.FullName = original.FullName;
//...
}
}
Then you can do:
return customers.Select(i => new Customer(i) { IsVip = DetermineVip(i.Score)})
.ToList()
But the downfall here is you will be creating a new Customer object based on each existing object, and not modifying the existing object - this is why I have put "can" in quotes. I do not know if this is truly what you desire.
No, Linq was designed to iterate over collections without affecting the contents of the source enumerable.
You can however create your own method for iterating and mutating the collection:
public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach(T item in enumeration)
{
action(item);
}
}
You can then use as follows:
return customers.ToList()
.ForEach(i => i.IsVip = DetermineVip(i.Score))
.ToList();
Note that the first ForEach will clone the source list.
As customers already is a List, you can use the ForEach method:
customers.ForEach(c => c.IsVip = DetermineVip(c.Score));

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