Sum and grouping using linq - c#

I have been trying to compile data from a table that looks like image1 and convert and display it to a table like image2.
Image1
Image2
First I figured I would do like this:
One class called Region that contains a list of the class called server
The server class would have 3 properties serverid, servername and a list of the class called CostData.
CostData would store year-month and sum of the cost for the month.
I have managed with linq to get a query that gives me the total for each server per month, like:
Year 2013, Month: 1, Server: Server1, Total: 460
Still I find myself short of a few things. First I need to put each servers monthly totals in one place, like what I've tried with my CostData, so that I with html can just iterate each servers month and display them in columns.
Also, with this solution I have still missed the CostDesc column which I also want to display the monthly total for like you can se on image2.
Here are my classes:
public class Region
{
public List<Server> Servers { get; set; }
}
public class Server
{
public string ServerID { get; set; }
public string ServerName { get; set; }
public List<CostData> MonthlyCosts { get; set; }
}
public class CostData
{
public string Date { get; set; }
public double Sum { get; set; }
}
This is how I am building my objects:
Region r = new Region();
r.servers = new List<Server>();
foreach (var row in linqQueryResult)
{
Server s = new Server();
s.ServerID = row.ServerID;
s.ServerName = row.ServerName;
s.MonthlyCosts = new List<CostData>();
CostData cd = new CostData();
cd.Date = row.Year.ToString() + "-" + row.Month.ToString();
cd.Sum = row.ServerSum;
s.MonthlyCosts.Add(cd);
r.Servers.Add(s);
}
return View(r);
Do you have any pointers or suggestions? I'm hoping somebody with more experience could take a look and give me some advice, not asking for the entire solution although if you wish to do it I would not mind :)
I wonder if there isn't a good way to get the data I want using linq. The catch to this is that I only have access to a stored procedure that will only give me data like in image1. All the manipulation with the data must be done locally with c#, although that probably isn't such a bad thing.

You need to do what is commonly known as a PIVOT, or a crosstab. This should help with that: Is it possible to Pivot data using LINQ?
Then it appears you want to do a totaling function that adds special total rows, and then possibly a custom sort order. After you get the pivot done, the rest is fairly easy though.
The total rows can be achieved by doing a separate query after you've done your pivot to total the rows up (group by). Then union the two results together. If you add a sort column, then you can sort the results and finally get your finished resultset.

Related

How to Pivot a List<T> and assign to a GridView without the use of DataTable?

I am currently preparing my Application to transition over to MVC and in doing so have replaced all SQL statements with LINQ (EF) and removing all Datasets/DataTables, replacing them with strongly typed Lists.
I am stuck on one scenario where I need to pivot a strongly typed List<T> , after I pivot (number of columns produced vary) I am attempting to re-assign the results back to the GridView, keeping in mind that I don't want to use a DataTable.
I have looked at various examples where people are attempting to use ExpandoObject but I can't get it to work and continue to get this error:
The data source for GridView with id 'GridReport' did not have any properties or attributes from which to generate columns. Ensure that your data source has content.
The alternative would be to create some kind of class dynamically with properties getter and setter, would this be the right approach?
Given that eventually I will discard GridView too in MVC (controls not supported) I am now just thinking to maybe create an output just using HTML table? Since all I am doing is outputting the display and not using the GridView for any other purpose.
Some guidance and code example would help for the right scenario.
My List <T> looks like this (shortened for simplicity) and I pivot on ticker_id using a GroupBy. Am I able to return the property names from the Linq query too? if so how?:
public class CorporationCompare
{
public int ticker_id { get; set; }
public string tickerSymbol { get; set; }
public decimal? price { get; set; }
}
//pivot
var query = (from item in lstCompareCorp
let key = new { ticker_id = item.ticker_id }
group new { tickerSymbol = item.tickerSymbol, price = item.price } by key)
.ToList();
Before Pivot:
ticker_id tickerSymbol price
1 GOOG 123.45
208 AAPL 543.21
After Pivot:
ticker_id 1 208
tickerSymbol GOOG AAPL
price 123.45 543.21

using string.split in entity to traverse tree depth

i have the following self-referencing table
public partial class products_category
{
public long id { get; set; }
public string category_name { get; set; }
public string category_description { get; set; }
//self referencing to table id
public Nullable<long> Parent_Id { get; set; }
public string navPath {get; set; }
}
here string navpath contains all the leading parents for a child categroy, say:
"Clothes" = 1 Parent_id=null, navpath=""
"Silk" = 2 Parent_id=1 navpath="1"
"Silk Suit"=3 parent_id=2 navpath="1-2"
"Saree" =4 parent_id=3 navpath="1-2-3"
"Dress Material"=5 parent_id=1 navpath="1" and so on....
now as per this scenario i want to access the flattend tree for frther processing for a certain depth only say to level 2 or until level 4 depth of children associated with navpath.
my idea regarding this issue was to approach using linq to ef this way:
var catTrees = db.products_category.Where(pc => pc.navpath.Split('-').Length < 4).ToList();
i am using the following link to do further traversing and tree generation:
https://bitlush.com/blog/recursive-hierarchical-joins-in-c-sharp-and-linq
and it is doing a great work so far, the only issue is i dont want to pre select whole table for processing. i want to achieve paging and a certain level of depth for first iteration, so i can maintain performance in case of thousand of records. [think of this as a category hierarchy or blog/youtube comments hierarchy].
but using the above ef linq command is giving the following error:
The LINQ expression node type 'ArrayLength' is not supported in LINQ to Entities.
i checked with ef docs and other places in SO to know that string.split doesn't work with EF implicitly. but can we apply it using extension methods or can this tree selection have alternate approach without using string.split and hitting DB only ones?
please advice.
This looks like an issues with building SQL code out of your LINQ mpre specifically SQL which takes a string splits it on dash and counts the elements.
if you dont hate the idea of loading into memory then you can force anything :)
var catTrees = db.products_category.ToList().Where(pc => pc.navpath.Split('-').Length < 4).ToList();
The trick here is to force the execution of the SQL by adding the .ToList() when we want the data from the database. This is called realizing the data.
Even with that realization trick the count is faster
var catTrees = db.products_category.ToList().Where(pc => pc.navpath.Count(a => a == '-') < 3).ToList();
these solutions are essentially the same as
List<Result> filter() {
List<Result> r = new List<Result>();
foreach(var a in db.products_category) {
if(a.navpath.Count(a => a == '-') < 3) {
r.add(a);
}
}
return r;
}
When thinking about it the filter method is somewhat less memory intensive as it reads one and one and never stores everything in memory. (in theory at least, only a few really knows what the .NET compiler does in the shadows)
I would advice you against using the navpath for checking depth.
If you can change your model, you could add an additional numeric Depth field for each category and populate it according its navpath, then you could select them from your code in this way:
var catTrees = db.products_category.Where(pc => pc.Depth < 3).ToList();
There are many ways to populate that new column, but the bottom line is that you will have to do it just once (given that you keep track of it every time you modify the navpath of a category).
One possible way of populating it would be looping through all categories, something like:
var allCategories = db.products_category.ToList();
foreach(var category in allCategories)
{
var depth = category.navpath == "" ? 0 : category.navpath.Split('-').Length + 1;
category.Depth = depth;
}
allCategories.SubmitChanges();

asp:gridview with a list as datasource, which also has a dictionary

I have a table with closing codes, for example:
Code Description
CL1 Reason 1
CL2 Reason 2
AF1 After 1 period
AF2 After 2 periods
Also a table with orders. Orders will be imported once a week and the table has, besides the other columns, a column holding the batch date and one holding the closing code. This last one can be NULL when the order isn’t closed or has one of the codes from the first given table.
I want to have a gridview which list per batch the total number of orders, the number of closed orders and per reason the number of orders involved.
Already made a class like this:
public class BatchSales
{
public DateTime BatchDate { get; set; }
public int BatchCount { get; set; }
public int TotalClosed { get; set; }
public Dictionary<string, int> Counters { get; set; }
}
And a method which returns a List of BatchSales.
Defined an asp:gridview in my aspx-page, with AutoGenerateColumns=false. I like to get this filled with 5 columns (batch date, count, closed, CL1, CL2, AF1 and AF2) and per row the relevant data.
In my code behind already added the first 3. My problem is adding the columns based on the dictionary. I’ve the closing codes read in a list and doing this:
foreach (var item in reasons)
{
BoundField bf = new BoundField();
bf.HeaderText = item.Description;
bf.DataField = "xxxx";
bf.DataFormatString = "{0:n0}";
bf.HeaderStyle.VerticalAlign = VerticalAlign.Top;
bf.ItemStyle.VerticalAlign = VerticalAlign.Top;
bf.ItemStyle.Wrap = false;
gvBatch.Columns.Add(bf);
}
What do I need to define for xxxx to get the value from the dictionary? Or isn’t this the right approach?
What you're wanting is more of a pivot grid. That aside, you need either an item template in the dictionary column that shows your dictionary data as rows (not ideal) or what I would do in this case is just have a button in that column that said something like "Show Details" and either have a modal window pop up with the list of counts or send it to a details page with the list of counts.

Sum total amount per product per date and bind it to a gridview programatically WPF & LINQ

The scenario:
I need to essentially build a gridview from code that will have binding set for each column while my data consists of amount of each product that manufactured each day (between specific dates but I got that covered).
The Problem:
I know how to bind through XAML with a class that has specific properties but this thing will be dynamic, since I don't know what products were manufactured in these specific dates and there is no guarantee that all of them were. so I need to build a dynamic (thought about ObservableCollection) option that will let me add items with their binding to my simple class inside an ObservableCollection (or whatever you think is best for this).
Current code:
This is the simple class I use to have the products and the amounts and that should be for each date:
public class MonthlyProducts
{
public string ProductID { get; set; }
public int Amount { get; set; }
}
Now this is how I know which products were indeed manufactured:
var ProductsInDates =
from Production in context.production
where Production.ProductionDate >= dtpStartDate.SelectedDate && Production.ProductionDate <= dtpEndDate.SelectedDate
group Production by Production.ProductID into Products
select new
{
ID = Products.Key
};
This is how I build the table, and here is where I'm not sure about how to bind the data through code:
foreach (var item in ProductsInDates)
{
MonthlyProducts temp = new MonthlyProducts();
temp.ProductID = item.ID;
temp.Amount = 0;
MonthObservable.Add(temp);
GridViewColumn TempColumn = new GridViewColumn();
TempColumn.DisplayMemberBinding = new Binding(temp.ProductID);
TempColumn.Header = temp.ProductID;
TempColumn.Width = 100;
MonthlyGridView.Columns.Add(TempColumn);
}
Now comes the hard part, This is where I get all the Amounts of the products that were manufactured (or shell I say not getting it yet since I don't know how to do it):
var MonthProduction =
from Production in context.production
where Production.ProductionDate >= dtpStartDate.SelectedDate && Production.ProductionDate <= dtpEndDate.SelectedDate
group Production by Production.ProductionDate into MonthlyReport
select new
{
DateID = MonthlyReport.Key,
Row = MonthlyReport
};
This is just me trying to play with this.
Basically I need to know if the Binding I did above to the data column will work since I can't check it yet, and I need to know how to sum each product for each date in this last LINQ query.
I hope I didn't forget anything, if so let me know and I'll provide all the info needed.

Accepted collection for working with Excel data

I have a bunch of data that I'm pulling into my application which frankly is best represented as an Excel spreadsheet. By this I mean:
There are a lot of columns which need 'summing up'
There is a reasonable amount of data (basically a sheet of numbers)
At the moment this is just raw data in a database, but I also have a spreadsheet which shows this data (along with formulas that I need to replicate in my app).
At the moment I've just got a List<of T> of each row, however I believe there might be a better collection for storing data of this type. I basically need to be able to manipulate these numbers easily.
Any suggestions?
One option would be to use a DataTable which also has a builtin aggregation method.
For example(from MSDN):
// Presumes a DataTable named "Orders" that has a column named "Total."
DataTable table;
table = dataSet.Tables["Orders"];
// Declare an object variable.
object sumObject;
sumObject = table.Compute("Sum(Total)", "EmpID = 5");
Another advantage is that it supports LINQ queries with LINQ-To-DataSet.
If your "excel data" can be represented in models, I'd just use models. For example like so:
public class ExcelModel()
{
public string Id { get; set; }
public double value1 { get; set; }
public int value1 { get; set; }
}
Then you can easily create a List<ExcelModel>, and get the total like so:
List<ExcelModel> model = repository.GetAll(); //just an example
var total = model.sum(x => x.value1);

Categories