Linq query need correct sum - c#

I am using .NET 4 Data Visualization Charts and am using a Linq query to get the data for the chart. I am new to Linq so not sure on how to word this query. Here is what it does, I have the follwoing tables its pulling data from
Orders
ShipmentSchedule
The orders table has the Product that was ordered, the OrderId, the amount that was shipped and the remaining quantity for that order. One order can only have a single product in it. I am trying to get all the Ordershipments in the class OrderShipment, make a List<> for it and group it by each product and show the total quantity for each product that was ordered and what has been shipped. The query below sums up the quantity shipped for all orders which is correct but because it is making a list of OrderShipments, I want the remaining quantity for each order across all shipments and sum that up by the product. Right now its adding up the remaining quantity for all the shipments which is wrong since an Order has the same Remaining Quantity across all shipments. how can I get the remaining quantity for each product so thw query adds up the Remaining quantity by each order correctly?? Please provide an example with code how to accomplish this if you have a suggestion, your helps really appreciated, thanks
private class ChartDataPoint
{
public string ProductName { get; set; }
public double QtyRemaining { get; set; }
public double QtyShipped { get; set; }
}
private class OrderShipment
{
public string ProductName { get; set; }
public double QuantityRemaining { get; set; }
public double QuantityShipped { get; set; }
public int OrderId { get; set; }
}
List<OrderShipment> productsOrdered =
(from os in Statistics.OptimalShipments
from o in Statistics.OrdersPlaced
where ((os.Date >= Statistics.ShippingWindowStartDate) &&
(os.Date <= Statistics.ShippingWindowEndDate) &&
(os.OrderId == o.OrderId) &&
((o.ClientLocationId == ClientLocation.ClientLocationId)))
select new OrderShipment()
{
ProductName = o.Product.Name,
QuantityRemaining = o.RemainingQuantity,
QuantityShipped = os.QuantityShipped,
}).ToList();
var query = productsOrdered.GroupBy(p => p.ProductName);
List<ChartDataPoint> chartDataPoints = new List<ChartDataPoint>();
foreach (var productGroup in query)
{
chartDataPoints.Add(new ChartDataPoint()
{
ProductName = productGroup.Key,
// This is obv wrong this sums up the Remaining quantity across
// all shipments for a order when we should be only taking the
//Remaining quantity once for each order across all shipments.
QtyRemaining = productGroup.Sum(po => po.QuantityRemaining),
QtyShipped = productGroup.Sum(po => po.QuantityShipped)
});
}

As I understand it your productGroup should have a lot of OrderShipment objects grouped by their product name. You then want to sum the QuantityShipped and the QuantityRemaining should all be the same and you want to just take this value?
If so then this should do you:
QtyRemaining = productGroup.First();
If I have misunderstood you may want to simplify the explanation, perhaps with an example...

Related

Linq : Group by and sum based on another list

I have list with below data (fetched from Json column)
public class ProductType
{
public string FieldName{ get; set; }
public List<string> items { get; set; }
}
List contains
Cart item1
item2
item3
wish item4
item2
item6
I have an another List which contains values related to ProductType
public class ProductCost
{
public string item { get; set; }
public int Amount{ get; set; }
}
List contains
item1 50
item2 60
I need to find sum of each item based on ProductTypes' FieldName column
Now I want to retrieve something like below
A list contains
cart sum(Amount of each items eg:110)
wish sum(amount of each items eg:60)
I'm sorry, I'm new to LINQ. Can someone help me or share resources where I can get a hint to achieve this? With a for loop I achieved this .But is there any way to find in one linq execution
You can use following query using Join, GroupBy and Sum:
List<ProductType> allProductTypes = new List<ProductType>(); // fill
List<ProductCost> allProductCosts = new List<ProductCost>(); // fill
var costsQuery = from pt in allProductTypes
from ptItem in pt.items
join pc in allProductCosts on ptItem equals pc.item
group (pt, ptItem, pc) by pt.FieldName into fieldGrp
select (FieldName: fieldGrp.Key, SumAmount: fieldGrp.Sum(x => x.pc.Amount));
List<ProductCost> totalCosts = costsQuery
.Select(x => new ProductCost {item = x.FieldName, Amount = x.SumAmount})
.ToList();

How to calculate sum for specific property without grouping main list data

I'm fetching Invoices from database and I want to return all invoices without grouping them!
I don't want to group them since If there are 100 invoices I want to return all of them 100, considering that I want to get Sum of Amount.
So it is perfectly fine to repeat same Total for multiple invoices if their sum is the same, so basically I want to calculate sum of Amount of each invoice item and group by CompanyId, PackageId, BankId, PayMethod only if it's possible?
-- Read code comments --
var result = await _dbContext.Invoices
.Where(p => p.CheckDate >= startDate && p.CheckDate <= endDate)
.Select(p => new DemoDto()
{
CompanyId = p.CompanyId,
Title = p.Title,
Price = p.Price
Total = p.Sum(p => p.Amount).ToString(), // Can I sum here and group by fields I mentioned above? without grouping all data set because I want to keep all 100 records if I received all 100 from database
})
.ToListAsync();
This query obliviously doesn't work because it says
Invoice does not contain definition for Sum and no accessible method..
DemoDto looks like this:
public class DemoDto
{
public string CompanyId {get;set;}
public string Title {get;set;}
public decimal Price {get;set;}
public decimal Amount {get;set;}
}
Invoice class looks like this:
public class Invoice
{
public string CompanyId { get; set; }
public int PackageId {get; set;}
public int BankId {get;set;}
public int PayMethod {get;set;}
public string Title { get; set; }
public decimal Price { get; set; }
public decimal Amount { get; set; }
}
what I'm missing here?
How can I achieve this?
Thanks guys
Cheers
Fetch all the invoices from the database:
var invoices = await _dbContext.Invoices
.Where(p => p.CheckDate >= startDate && p.CheckDate <= endDate)
.ToListAsync();
Group the in-memory results using Linq-To-Object:
var result = invoices?
.GroupBy(p => new { p.CompanyId, p.PackageId, p.BankId, p.PayMethod })
.SelectMany(x => x.Select(y =>
new DemoDto
{
CompanyId = y.CompanyId,
Title = y.Title,
Price = y.Price,
Total = x.Sum(z => z.Price)
}))
.ToList();
If you want to perform the grouping in the database for some reason, you should execute a raw SQL query or a stored procedure rather than relying on the ORM to generate some magic (and most probably inefficient) query for you.

LINQ contains in a list within a list

I'm trying to use a contains in a list within a list but been stuck on this one:
var postFilter = PredicateBuilder.False<Company>();
// Loop through each word and see if it's in the company's facility
foreach (var term in splitSearch)
{
var sTerm = term.Trim();
postFilter = postFilter.Or(x =>
x.Facilities.Contains(y=>
y.Facility.Name.ToUpper().Contains(sTerm)) ||
x => x.Facilities.Contains(y =>
y.Facility.Description.ToUpper().Contains(sTerm)));
}
Postfilter is a list of companies, a company has a list of companyfacility items which is a 1:m relationship. A facility also has a 1:m relationship with this table. The x thus represents a companyfacility object. The y should represent a facility object.
(So a company can have many facilities, and a facility can belong to many companies. In between i use the companyfacility table for additional information of that companies facility - example, a specific company can have a lathe table that goes to diameter 300 where other companies would go to diameter 250, so it's important to have the table in between)
I want to return the companies which have the sTerm in their facility name or facility description, but this linq statement is invalid.
Thanks for the help!
Here is the LINQ and some example code:
void Main()
{
List<Company> postFilter = new List<UserQuery.Company>();
var sTerm = "XXX".Trim().ToUpper();
postFilter = postFilter.Where(x =>
x.Facilities.Any(f => f.Name.ToUpper().Contains(sTerm)
|| f.Description.ToUpper().Contains(sTerm))).ToList();
}
public class Company
{
public string CompanyName { get; set; }
public List<Facility> Facilities { get; set; }
}
public class Facility
{
public string Name { get; set; }
public string Description { get; set; }
}

Create Poll in C# (windows form)

I want create a poll. i have a table for questions(Fields: ID,Question) and another for answers(Fields: ID,QuestionID,Answer). There is a table for the results(fields: QuestionID,AnswerID,UserID).i want to show the Percent of responding to any item in datagridview.
for example when i enter question id, datagridview show:
Choose question ID:1
Option...........Percent
1 ------------------------- 30
2 -------------------------- 20
3---------------------------50
4-------------------------- 10
this is my code But the result does not show it:
int a = Convert.ToInt32(textBox1.Text);
var q = (from s in Session.DB.PoolUsers
where s.PoolQID == a
select s);
var qq = (from c in q
group c by c.PoolAID into agroups
select agroups.Key);
var qqq = (from c in qq
select c).Count();
MessageBox.Show(qqq.ToString());
And there is my classes:
public partial class PoolA //For answers
{
public int Id { get; set; }
public string Answer { get; set; }
public string QuestionID { get; set; }
}
public partial class PoolQ //Questions
{
public int Id { get; set; }
public string Question { get; set; }
public string AnswerID { get; set; }
public string Status { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
}
public partial class PoolUser //resaults
{
public int Id { get; set; }
public int PoolQID { get; set; }
public int PoolAID { get; set; }
public int UserID { get; set; }
}
This is not the optimal way I would do it, I would highly consider you, to change your entities.
Here is the solution:
int questionId = 1;
var questionAnswers = list.Where(elem => elem.PoolQID == questionId);
int questionAnswersCount = questionAnswers.Count();
var answersPrecentage = questionAnswers
.GroupBy(elem => elem.PoolAID)
.ToDictionary(grp => grp.Key, grp => (grp.Count() * 100.0 / questionAnswersCount));
.NET Fiddle Link
I don't know your intentions but I think you should redesign the schema (not that it not doable as it is) for maximum efficiency. You call your PoolUser table your results table but it is your answers log at best. you need a table that has the results processed already such as would be done in a DWH kind of situation.
If you have a results summary table you could work out vertical sums groups all day without the extra performance of joining tables etc. for ad hoc data. I reckon you'd be running this a lot to go over the results, so it sounds like it is a better idea to store the summarized information for reporting, performance etc.
as for the current state of the problem:
var qq = (from c in q
group c by c.PoolAID into agroups
select agroups.Key);
agroups has the answerIds grouped up for the question number you typed in.
then you are just showing the row count of the grouped up answers which shows you 2:
var qqq = (from c in qq
select c).Count();
you are telling me that your messagebox shows the number "2"
this means that people only selected 2 options to answer whatever question id you put in. you don't know whether the answer is correct or not. the last query "qqq" you have, you have the count outside so it only returns the row count. you don't want the row count. you want the answerID count after the grouping.
from c in qq
select count(c)
this is the total answers for the questionID you typed in.
now you need the correct answers in order to calculate the percentage. this is the query missing. you have gone 75% of the way. just finish it off. hopefully this is sufficient detail and not too much text to read.
The problem was solved and I got the following code. I also draw a graph based on
private void button1_Click(object sender, EventArgs e)
{
var list = (from c in Session.DB.PoolUsers
select c).ToList();
int questionId = Convert.ToInt32(textBox1.Text);
var questionAnswers = list.Where(elem => elem.PoolQID == questionId);
int questionAnswersCount = questionAnswers.Count();
var answersPrecentage = questionAnswers
.GroupBy(elem => elem.PoolAID)
.ToDictionary(grp => grp.Key, grp => (grp.Count() * 100.0 / questionAnswersCount));
dataGridView1.DataSource = answersPrecentage.ToArray();
//Draw Chart From DataGridview:
chart1.DataBindTable(answersPrecentage);
}
And works properly
Thanks all :)

Get a list of all the items based on the latest entry

I have this collection of data shaped like this:
public class PriceList
{
[Display(Name = "Price List ID")]
public int m_ID { get; set; }
[Display(Name = "Price List Provider")]
public int m_Provider { get; set; }
[Display(Name = "Current Book")]
public int m_BookID { get; set; }
public BookInfo m_Book { get; set; }
[Display(Name = "Price")]
public decimal m_Price { get; set; }
[Display(Name = "Date of price list")]
public DateTime m_PriceListDate { get; set; }
}
A Price list is a price list object created when I get a price for a particular item in my mvc-app. It can be created many times over a same day, week, month, or year.
So now a book can be part of a category, for example "Comic book". And my problem is that I am trying to create a method that will give me only the LATEST entry, so the most recent date only, of all items based on this collection.
Here's the method:
public List<PriceList> ListLatestPriceListByBookCategoryID(int _id)
{
List<PriceList> listToReturn = new List<PriceList>();
var priceListQry = from pl in m_Db.PriceList
where pl.Book.BookCatID == _id
// INSERT CODE HERE???
select pl;
if (!priceListQry.Any())
{
return null;
}
listToReturn.AddRange(priceListQry);
return listToReturn;
}
Basically, I want to created a query that will return only the needed data. And this needed data consist of only the most recent entry of each book found by the id. I can have many category and many books.
Can anyone help me out figure a good way to do this? Up to now all I've though about was a massive method that would iterate through the whole list to filter all unnecessary data, but I'd like something more efficient because I will eventually have a mass of data and this will prove to take a lot of time.
* EDIT *
As of now I have added this line of code:
var result = m_Db.PriceList.GroupBy(r => r.BookID).Select(r => new
{
BookID = r.OrderByDescending(t => t.PriceListDate).First().BookID,
PriceListDate = r.OrderByDescending(t => t.PriceListDate).First().PriceListDate
});
And I have no error on this line of code and it makes sense to me. However, the listToReturn.AddRange(result) line now throw this error:
Error 13 Argument 1: cannot convert from 'System.Linq.IQueryable<AnonymousType#1>' to 'System.Collections.Generic.IEnumerable<MyApp.Models.Entities.PriceList>
You can try:
var result = m_Db.PriceList.GroupBy(r => r.m_BookID)
.Select(r=> new
{
BookID = r.OrderByDescending(t=> t.m_PriceListDate).First().m_BookID,
Date = r.OrderByDescending(t=> t.m_PriceListDate).First().m_PriceListDate,
});
(Not really sure how much efficient that would be)

Categories

Resources