Sum of items in a collection - linq

Using LINQ to SQL, I have an Order class with a collection of OrderDetails. The Order Details has a property called LineTotal which gets Qnty x ItemPrice.
I know how to do a new LINQ query of the database to find the order total, but as I already have the collection of OrderDetails from the DB, is there a simple method to return the sum of the LineTotal directly from the collection?
I'd like to add the order total as a property of my Order class. I imagine I could loop through the collection and calculate the sum with a for each Order.OrderDetail, but I'm guessing there is a better way.

You can do LINQ to Objects and the use LINQ to calculate the totals:
decimal sumLineTotal = (from od in orderdetailscollection
select od.LineTotal).Sum();
You can also use lambda-expressions to do this, which is a bit "cleaner".
decimal sumLineTotal = orderdetailscollection.Sum(od => od.LineTotal);
You can then hook this up to your Order-class like this if you want:
Public Partial Class Order {
...
Public Decimal LineTotal {
get {
return orderdetailscollection.Sum(od => od.LineTotal);
}
}
}

Related

How to get top 10 customers based on values of orders

I have a list of Customers who each have a list of Orders. Each Order has a list of LineItems.
I would like to write a LINQ query that would get me the top 10 customers based on order value (i.e. money spent) and not the total number of orders.
One customer could have 2 orders but could have spent £10,000, but another customer could have 100 orders, and only spent £500.
Right now, I have this which gets me the top 10 customers by the number of orders.
var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
let activeCount = c.SaleOrders.Count(so => so.Status != SaleOrderStatus.Cancelled)
orderby activeCount descending
select c).Take(10);
UPDATE
Thanks to Jon Skeet's comment about doing a double Sum, I wrote the following query which compiles.
var customers = (from c in _context.Customers where c.SaleOrders.Count > 0
let orderSum = c.SaleOrders.Where(so => so.Status != SaleOrderStatus.Cancelled)
.Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))
orderby orderSum descending
select c).Take(10);
But when I run this, I get the following error:
It seems LINQ doesn't recognise my .CalculateTotal() method which sit on my LineItem.cs entity.
The problem you were seeing is that CalculateTotal() is not something that Linq can translate into SQL (which is done at run-time, hence no complier error).
The essential problem here is that Linq doesn't really work on lambdas (Func<>), but actually Expressions (Expression<Func<>>), which is the code in a partial compiled state, which Linq then goes about disassembling and translating into SQL.
So, let assume CalculateTotal is a member function defined like this:
public decimal CalculateTotal()
{
return this.quantity * this.value;
}
We could define that as a local lambda function
Func<LineItem, decimal> CalculateTotal = (li => li.quantity * li.value);
Now, we have a lambda which takes a LineItem and returns a value, which is exactly what Sum() wants, so now we can replace:
.Sum(so => so.LineItems.Sum(li => li.CalculateTotal()))
with
.Sum(so => so.LineItems.Sum(CalculateTotal))
But that will crash, just as it did before, because, as I said, it wants an Expression. So, we give it one:
Expression<Func<LineItem, decimal>> CalculateTotal = (li => li.quantity * li.value);

LINQ Distinct set by column value

Is there a simple LINQ query to get distinct records by a specific column value (not the whole record)?
Anyone know how i can filter a list with only distinct values?
You could use libraries like morelinq to do this. You'd be interested in the DistinctBy() method.
var query = records.DistinctBy(record => record.Column);
Otherwise, you could do this by hand.
var query =
from record in records
group record by record.Column into g
select g.First();
Select a single value first and then run the Distinct.
(from item in table
select item.TheSingleValue).Distinct();
If you want the entire record you need to use group x by into y. You then need to find a suitable aggregate function like First, Max, Average or similar to select one of the other values in the group.
from item in table
group item by item.TheSingleValue into g
select new { TheSingleValue = g.Key, OtherValue1 = g.First().OtherValue1, OtherValue2 = g.First().OtherValue2 };
You could make an implementation of the IEqualityComparer interface:
public class MyObjectComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
return x.ColumnNameProperty == y.ColumnNameProperty;
}
public int GetHashCode(MyObject obj)
{
return obj.ColumnNameProperty.GetHashCode();
}
}
And pass an instance into the Distinct method:
var distinctSource = source.Distinct(new MyObjectComparer());

Linq - How to check if an object exists in database

I have an in memory List of objects. I want to check if each one exists in a database and if not, set a bool property on that object to true.
Object
class Part
{
public bool NewPart { get; set; }
public string PartNumber { get; set; }
public string Revision { get; set; }
public string Description { get; set; }
}
List contains the collection of parts. For each part, if it exists in the database then NewPart should be set to FALSE, else TRUE. I'm looking for the most efficient way to do this as there are likely to be hundred of parts so I'm thinking that running a SQL query for each part may not be the most efficient method.
Ideas on the best way to achieve this appreciated.
It depends on which ORM you are using, but with Linq2Sql you can use a query like:
var query = from p in db.parts
where myList.Contains(p.PartNumber)
select p.PartNumber;
You can then use the IEnumerable returned to set your newPart field
As an alternative, if your ultimate goal is to do an Upsert type action, then check out this question and its answers Insert Update stored proc on SQL Server (needs SQL level implementation, not linq)
The following will only hit the database once.
var myList = (from r in parts select r.PartNumber).ToList();
var existingParts = (from r in dc.Parts
where myList.Contains(r.PartNumber) select r.PartNumber).ToList();
foreach(var r in parts)
r.NewPart = existingParts.Contains(r.PartNumber);
Note, the generated sql could very well be something like
SELECT PartNumber
FROM Parts Where PartNumber in (#p0, #p1, #p2, #p3 .... )
so this should work if the parts list of a hundred or so, but not if it is over 2100.
This is one of those cases where the most efficient approach depends upon the actual data.
The first obtains all partNums from the database:
HashSet<int> partNums = new HashSet<int>(from p in GetTable<DBPart> select p.PartNumber);
foreach(var p in parts)
p.NewPart = partNums.Contains(p.PartNumber);
The second queries the database with the relevant partNumbers:
HashSet<int> partNums = new HashSet<int>(
from p in GetTable<DBPart> where (from mp in parts select mp.PartNumber).Contains(p.PartNumber) select p.PartNumber);
foreach(var p in parts)
p.NewPart = partNums.Contains(p.PartNumber);
The former will be more efficient above a certain number of rows in the database, and less efficient above it, because the latter takes a longer time to build a more complicated query, but the former returns everything.
Another factor is the percentage of hits expected. If this number is relatively low (i.e. only a small number of the parts in the list will be in the database) then it could be more efficient to do:
Dictionary<int, Part> dict = partsSource.ToDictionary(p => p.PartNumber, p);
foreach(int pn in
from p in GetTable<DBPart> where (from kv in dict select kv.Key).Contains(p.PartNumber) select p.PartNumber);
dict[pn].NewPart = true;
Where partsSource is the means by which the List parts was obtained in the first place, here instead of obtaining a list, we obtain a dictionary, which makes for more efficient retrieval of those we want. However, it we're going to obtain parts as a list anyway, then we can't really gain here, as we use slightly more effort building the dictionary in the first place, than iterating through the list.

Linq, how can I do flexible sorting?

I'm new to linq, now I need to do flexible sorting with sort parameter specified.
but
var query =
from accessdoc in dt1.AsEnumerable()
join content in dt2.AsEnumerable()
on accessdoc.Field<string>("name") equals content.Field<string>("FileName")
into docs
orderby accessdoc.Field<DateTime>("CreateDate") descending //TODO: HOW TO SORT??
dose not meet the demand.
Can I be helped out here?
Since linq is late binding you can do your query and then apply your sort separately. If you can split up how you do the sort parameter slightly, you could do something like this: (this code hasn't been compiled, so please bear with me)
public enum SortDirection
{
Ascending = 0, //default value
Descending = 1
}
now if you pass in the linq expression and the direction, you could do something like this:
public IQueryable<MyObject> GetWithSort(System.Linq.Expressions.Expression<Func<MyObject, TKey>> sortExpression, SortDirection direction)
{
var results = from accessdoc in dt1.AsEnumerable()
join content in dt2.AsEnumerable()
on accessdoc.Field<string>("name") equals content.Field<string>("FileName")
into docs
select...;
if (direction == SortDirection.Descending)
return results.OrderByDescending(sortExpression);
return results.OrderBy(sortExpression)
}
select... will have to be replaced with however you are selecting your objects out of the linq statement.

How do I use LINQ to obtain a unique list of properties from a list of objects?

I'm trying to use LINQ to return a list of ids given a list of objects where the id is a property. I'd like to be able to do this without looping through each object and pulling out the unique ids that I find.
I have a list of objects of type MyClass and one of the properties of this class is an ID.
public class MyClass
{
public int ID { get; set; }
}
I want to write a LINQ query to return me a list of those Ids.
How do I do that, given an IList<MyClass> such that it returns an IEnumerable<int> of the ids?
I'm sure it must be possible to do it in one or two lines using LINQ rather than looping through each item in the MyClass list and adding the unique values into a list.
IEnumerable<int> ids = list.Select(x=>x.ID).Distinct();
Use the Distinct operator:
var idList = yourList.Select(x=> x.ID).Distinct();
Using straight LINQ, with the Distinct() extension:
var idList = (from x in yourList select x.ID).Distinct();
When taking Distinct, we have to cast into IEnumerable too. If the list is <T> model, it means you need to write code like this:
IEnumerable<T> ids = list.Select(x => x).Distinct();
int[] numbers = {1,2,3,4,5,3,6,4,7,8,9,1,0 };
var nonRepeats = (from n in numbers select n).Distinct();
foreach (var d in nonRepeats)
{
Response.Write(d);
}
Output
1234567890

Resources