Accepted collection for working with Excel data - c#

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);

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

C# CSV to Tuple (or useful data type)?

I work for a school district and we are having to manually create user logins (AD), and GAFE accounts. We would like automate this as much as we can. Currently, We have a CSV file that is exported daily from our SIS (Student information system) that has a list of all new students and I need to read that data, apply some formulas, and output two CSVs, one for GAFE and one for AD, with the results from my formulas.
My thoughts are to read the CSV and save it into a tuple data type, then write a new tuple with the output I need, then save to new CSVs. I thought tuple would work nicely, but I'm still new to C# that I'm not sure what would work best. If you guys have any recommendation on other data types I would love the input.
Here's the header-
"SchoolName","firstName","middleName","lastName","grade","studentNumber","Change","startDate","endDate","EnrStartStatus","CalcStartStatus","DateAdded"
"AHS","John","Smith","Doe","12","1779123445","New Student at School","2016-11-29 00:00:00","","","","2016-11-22 20:00:00"
So, I'm having some mental logic issues. I'm not sure on how to convert the CSV to tuple without having to do nested foreach loops (the way I'm thinking about going about it doesn't seem efficient.). I figured that there would be a library or something built into C# that would make it so much easier... Any input that is given would greatly be appreciated.
Thanks,
Throdne
There are several really powerful libraries to most of the work for you. One really good one is CSVHelper which will not only read and write the data for you, but perform type conversions so that your numbers and dates are stored as numbers and dates.
Given sample data similar to yours:
"FirstName","MiddleName","LastName","Grade","StudentNumber","EnrollDate"
"Ziggy","V.","Aurantium","12","4001809","12/13/2016 6:18:21 PM"
"Nancy","W.","Stackhouse","11","9762164","12/15/2016 7:06:20 PM"
"Sullivan","N.","Deroche","11","7887589","12/11/2016 1:31:50 PM"
1. Devise a class for the data
public class Student
{
public int StudentNumber { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public int Grade { get; set; }
public DateTime EnrollDate { get; set; }
public Student()
{ }
}
2. Load the Data
// a form/class level collection for the data
List<Student> myStudents;
Then to load the data:
using (var sr = new StreamReader(#"C:\Temp\students.csv", false))
using (var csv = new CsvReader(sr))
{
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.QuoteAllFields = true;
myStudents = csv.GetRecords<Student>().ToList();
}
That's it: 3 lines of code. There are many other Configuration options to fine tune how it works. Also:
If there are a lot of rows, you can leave off the ToList() and work with the IEnumerable result and load each row as needed
If the Property names you want to use dont match the CSV header names, you can supply a Map to tell CSVHelper which fields map to which properties.
Ditto for when there are no field names.
Exporting your collection to new output CSVs is just as easy as reading them
You would also probably need a Map (or two) to control the output order for the output CSVs.
Best of all, it converts the data types for you. No, wait, best of all is that it wont split up fields with embedded commas (as in "Ziggy","V.","Aurantium, II","12"... note the last name data) the way String.Split(',') will.
I recommend to use a string array instead of Tuples.
You can easily convert a line of csv values into a string array with this line of code:
line.Split( new char[] { '"', ',' }, StringSplitOptions.RemoveEmptyEntries );
This returns a string array.
Using " and , both as separator characters lets you get rid of the "'s in the same step.

Creating table at runtime

I'm new to databases and I'm not sure how to handle this situation. I have 3 tables connected this way:
Session <- 1:1 -> Document <- 1:1 -> DocumentData
So basically there is always 1 Session that has a Document which has a DocumentData.
I want to be able to add different types and columns of data to DocumentData, so for example I can have DocumentData with 3 columns of type DateTime,Int32,Int32. And then have another table with 5 columns of types Datetime,double,Int32,Int32,Int32. Basically what I'm going for is to have something like this in my code:
using(var unit = new UnitOfWork(new SessionContext()))
{
var data = unit.Sessions.GetCurrent().Document.DocumentData;
var row = data.Column[0].Rows[5]... etc.
}
This is because DocumentData is generated from csv specified by a user, so each DocumentData is made of different columns.
EDIT:
I want to know how to create a table on runtime and assign whatever columns I want to it. So I want to be able to do something like:
var doc = new Document();
session.Document = doc;
doc.Columns.Add(new Column() {Rows = rows});
doc.Columns.Add(new Column() {Rows = rows2});
doc.SaveChanges();
and then have second table with different columns.
EDIT2:
To make it more clear I want to convert this:
public class DocumentData {
public List<DocumentColumn> Columns { get; set; }
}
public class DocumentColumn {
public string ColumnName { get; set; }
public List<object> Rows { get; set; }
}
into ado.net entities so I can save them to database.
You can use a SQL statement to create tables at runtime (via dbcontext). I don't think that its possible to bind such a table to an entity / class at runtime after the database / context is initialized.
But if you don't have to use the different / variable columns as query / selection parameters, simply serialize the document class in a single BLOB column and your done.

Sum and grouping using linq

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.

Storing items from MySQL into muliple variables

I've written a SQL select statement that returns multiple fields from 1 record in the table.
Here is my statement:
-- (item_num being the PK)
SELECT item_num,
category,
weight,
cost,
description
FROM inv
WHERE item_num = #inumber;
How do I save each field into a variable?
I've seen samples written in while loops but my statement returns ints and chars so I would like to save them to the respectable variables and not an array.
Please bear with me as I'm new to working with database with coding. I just need to better understand the format.
I've searched for the answer but couldnt have anything related. Maybe my approach is all wrong.
Your help is appreciated.
Ultimately, you should be using a Datareader and from this it is possible to assign the values directly to the variables;
using (var rdr = db.ExecuteReader(cmd))
{
myIntValue = (int) rdr["IntValue"];
myStringValue = rdr["StringValue"].ToString();
}
However, in your case, my suggestion would be to use a DTO and populate these accordingly from your Data Layer
public class MyDTO
{
public int MyInt { get; set; }
public string MyString { get; set; }
}
and return IEnumerable<MyDTO>
First of all, your returns will not be ints and chars. It'll be strings. At least, that's my experience. And you should be storing them all in a MySQL datareader. This will allow you to do something like:
List<string> weight = new List<string>();
while(dataReader.Read())
{
weight.Add(dataReader["weight"]);
}
What this does is it will create a list of strings from all the results of your query that were under the "weight" column. From there, if you need them to be chars and ints, you can convert them. But I suggest you read up on MySQL datareaders. My example above isn't perfect, but it's a start.

Categories