Diplaying AVL Tree data in a GUI C# - c#

Basically I have an AVL tree that stores instances of Country class. When I do an inorder traversal of the tree, I am able to see the country details correctly, however I wish to view and modify instances of the country class in a GUI. The issue I am having is I have no idea how to access the class data and display it in something like a listbox. Here is my Country class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace International_Trading_Data
{
class Country : IComparable
{
public string countryName { get; set; }
public double gdp { get; set; }
public double inflation { get; set; }
public double tradeBalance { get; set; }
public int hdiRanking { get; set; }
public LinkedList<string> tradePartners { get; set; }
public string f;
public Country (){
}
public Country(string cname, double g, double i, double t, int h, LinkedList<string> tp)
{
this.countryName = cname;
this.gdp = g;
this.inflation = i;
this.tradeBalance = t;
this.hdiRanking = h;
this.tradePartners = tp;
}
public int CompareTo(object obj)
{
Country temp = (Country)obj;
return countryName.CompareTo(temp.countryName);
}
public override string ToString()
{
foreach (string i in tradePartners)
f += i+",";
return countryName+" "+gdp+" "+" "+inflation+" "+tradeBalance+" "+ hdiRanking+ " "+f;
}
}
}
`
This is where I create instances of the country class:
public void loadFile()
{
OpenFileDialog open = new OpenFileDialog();
open.Filter = "CSV Files (*.csv)|*.csv";
open.FilterIndex = 1;
open.Multiselect = true;
if (open.ShowDialog() == DialogResult.OK)
{
string selectedFilePath = open.FileName;
const int MAX_SIZE = 5000;
string[] allLines = new string[MAX_SIZE];
allLines = File.ReadAllLines(selectedFilePath);
foreach (string line in allLines)
{
if (line.StartsWith("Country"))
{
headers = line.Split(',');
}
else
{
string[] columns = line.Split(',');
LinkedList<string> tradePartners = new LinkedList<string>();
string[] partners = columns[5].Split('[', ']', ';');
foreach (string i in partners)
{
if (i != "")
{
tradePartners.AddLast(i);
}
}
countries.InsertItem(new Country(columns[0], Double.Parse(columns[1]),Double.Parse(columns[2]), Double.Parse(columns[3]) ,int.Parse(columns[4]),tradePartners));
}
}
Here is the code for my inorder traversal:
public void InOrder()
{
inOrder(root);
}
private void inOrder(Node<T> tree)
{
if (tree != null)
{
inOrder(tree.Left);
System.Diagnostics.Debug.WriteLine(tree.Data.ToString());
inOrder(tree.Right);
}
This code produces the following output for a few test countries:
Argentina 3 22.7 0.6 45 Brazil,Chile,
Australia 3.3 2.2 -5 2 China,Japan,New_Zealand,
Brazil 3 5.2 -2.2 84 Chile,Argentina,USA,
So I know that my classes are bieng stored correctly in the avl tree.

I am not sure what you are using as a data structure for your countries collection, but assuming its a List for now, you can do the following (NOTE: this sample is just to demonstrate displaying information on a UI for manipulation):
public Form1()
{
InitializeComponent();
List<Country> countries = new List<Country>() {
new Country() { countryName = "Mattopia" , gdp = 1500, inflation = 1.5, f="hi"},
new Country { countryName = "coffeebandit", gdp = 2000, inflation = 1.2, f="hey" }};
listBox1.DisplayMember = "countryName";
listBox1.DataSource = countries;
}
public class Country
{
public string countryName { get; set; }
public double gdp { get; set; }
public double inflation { get; set; }
public double tradeBalance { get; set; }
public int hdiRanking { get; set; }
public LinkedList<string> tradePartners { get; set; }
public string f;
}
Then, you can use the selected index changed event to populate your fields:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
Country country = (Country)listBox1.SelectedValue;
//fill up all other GUI controls
textBox1.Text = country.f;
textBox2.Text = country.inflation.ToString();
}
And if you want to process text changes:
private void textBox1_TextChanged(object sender, EventArgs e)
{
Country country = (Country)listBox1.SelectedValue;
if (country != null)
{
country.f = textBox1.Text;
}
}
This will give you the following display:
This should demonstrate the basics of how to edit a class in a WinForms UI.
For more advanced examples, I would recommend using other Events to capture information as your needs change.

Related

How to fix the coding error 'input string was not in a correct format'

Here are the full details of my code:
public partial class Form1 : Form
{
List<Sales> sales = new List<Sales>();
BindingSource bs = new BindingSource();
public Form1()
{
InitializeComponent();
LoadCSV();
bs.DataSource = sales;
dgvSales.DataSource = bs;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void LoadCSV()
{
string filePath = #"c:\Users\demo\Task3_shop_data.csv";
List<string> lines = new List<string>();
lines = File.ReadAllLines(filePath).ToList();
foreach (string line in lines)
{
List<string> items = line.Split(',').ToList();
Sales s = new Sales();
s.TextBook = items[0];
s.Subject = items[1];
s.Seller = items[2];
s.Purchaser = items[3];
s.purchasedPrice = float.Parse(items[4]);
s.SalePrice = items[6];
s.Rating = items[7];
sales.Add(s);
}
}
}
}
my sales class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MichaelSACU301task3
{
internal class Sales
{
public string TextBook { get; set; }
public string Subject { get; set; }
public string Seller { get; set; }
public string Purchaser { get; set; }
public float purchasedPrice { get; set; }
public string SalePrice { get; set; }
public string Rating { get; set; }
}
}
I tried launching it but the error message keeps appearing can someone please help me fix this problem.
Use float.TryParse prior to assigning to purchasedPrice property, if the value can not be converted remember it in a list. In the example below the code to read file data is in a separate class which returns a list of sales and a list of int which is used to remember invalid lines where purchasedPrice data is invalid. You should also consider validating other data and also ensure proper amount of data after performing the line split.
public class FileOperations
{
public static (List<Sales>, List<int>) LoadSalesFromFile()
{
List<Sales> sales = new List<Sales>();
List<int> InvalidLine = new List<int>();
string filePath = #"c:\Users\demo\Task3_shop_data.csv";
List<string> lines = File.ReadAllLines(filePath).ToList();
for (int index = 0; index < lines.Count; index++)
{
var parts = lines[0].Split(',');
// validate purchase price
if (float.TryParse(parts[4], out var purchasePrice))
{
Sales s = new Sales();
s.TextBook = parts[0];
s.Subject = parts[1];
s.Seller = parts[2];
s.Purchaser = parts[3];
s.purchasedPrice = purchasePrice;
s.SalePrice = parts[6];
s.Rating = parts[7];
sales.Add(s);
}
else
{
// failed to convert purchase price
InvalidLine.Add(index);
}
}
return (sales, InvalidLine);
}
}
Call the above code in your form
var (salesList, invalidLines) = FileOperations.LoadSalesFromFile();
if (invalidLines.Count > 0)
{
// use to examine bad lines in file
}
else
{
// uses sales list
}
the error sis probably due the impossiability of float.Parse() parse the items[4] in float
you may track value of items[4] using brake point in VS

Related comboboxes, add a third combobox

I'm trying to do a related combobox. I already have 2 comboboxes, but now I want to add a third.
I have this code for the 2nd combo box.
I'm using windows forms.
The entire code: https://repl.it/#devilonline/MuddyPartialBytecode#main.cs
private string[] GetCastById(int id)
{
return nomes.Where(line => line.movies_id== id).Select(l => l.nomes).ToArray();
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
comboBox1.Items.Clear();
int id = nomes[comboBox1.SelectedIndex].id;
foreach (string name1 in GetCastById(id))
{
this.comboBox1.Items.Add(name1);
}
}
print
It is obvious that you are clearing the Items of the comboBox1 then try to get the id of the selected item, which should throw an exception because no item will be selected by then:
comboBox1.Items.Clear(); // here the items are cleared
int id = nomes[comboBox1.SelectedIndex].id; // nomes[comboBox1.SelectedIndex] = -1
Based on your database, the cast table is related to the movies so each movie has a corresponding list of cast, you should then get the id of the selected movie rather than the selected nome:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
comboBox1.Items.Clear();
int id = movies[comboBoxMovie.SelectedIndex].id; // here we used comboBoxMovie
foreach (string name1 in GetCastById(id))
{
this.comboBox1.Items.Add(name1);
}
}
This is a great opportunity to use databinding through a System.ComponentModel.BindingList<>. Below is a working example. Note, I've added get methods inside the classes for simplicity of populating the ComboBoxes for this example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
public class Form1
{
class Category
{
public int Id { get; set; }
public string Name { get; set; }
public Category(int id, string name)
{
this.Id = id;
this.Name = name;
}
public static List<Category> GetCategories()
{
return new List<Category>()
{
new Category(1, "Action"),
new Category(2, "Comedy")
};
}
}
class Movie
{
public int Id { get; set; }
public string Name { get; set; }
public int CategoryId { get; set; }
public Movie(int id, string name, int catId)
{
this.Id = id;
this.Name = name;
this.CategoryId = catId;
}
public static List<Movie> GetMovies()
{
return new List<Movie>()
{
new Movie(1, "Rambo", 1),
new Movie(2, "Delta Force", 1),
new Movie(3, "Elf", 2),
new Movie(4, "Space Balls", 2)
};
}
}
class Cast
{
public int Id { get; set; }
public string Names { get; set; }
public int MovieId { get; set; }
public Cast(int id, string names, int movieId)
{
this.Id = id;
this.Names = names;
this.MovieId = movieId;
}
public static List<Cast> GetCast()
{
return new List<Cast>()
{
new Cast(1, "Silvester Stalone", 1),
new Cast(2, "Chuck Norris", 2),
new Cast(3, "Will Farrell", 3),
new Cast(4, "John Candy", 4)
};
}
}
private BindingList<Category> _categoryBindingList = new BindingList<Category>();
private BindingList<Movie> _moviesBindingList = new BindingList<Movie>();
private BindingList<Cast> _castBindingList = new BindingList<Cast>();
private void Form1_Load(object sender, EventArgs e)
{
// Your database calls would replace these Get methods.
Category.GetCategories().ForEach(x => _categoryBindingList.Add(x));
Movie.GetMovies().ForEach(x => _moviesBindingList.Add(x));
Cast.GetCast().ForEach(x => _castBindingList.Add(x));
ComboBox1.DataSource = _categoryBindingList;
ComboBox1.DisplayMember = "Name";
ComboBox2.DataSource = _moviesBindingList.Where(x => x.CategoryId == (Category)ComboBox1.SelectedValue.Id).ToList();
ComboBox2.DisplayMember = "Name";
ComboBox3.DataSource = _castBindingList.Where(x => x.MovieId == (Movie)ComboBox2.SelectedValue.Id).ToList();
ComboBox3.DisplayMember = "Names";
}
private void ComboBox1_SelectedValueChanged(object sender, EventArgs e)
{
ComboBox2.DataSource = _moviesBindingList.Where(x => x.CategoryId == (Category)ComboBox1.SelectedValue.Id).ToList();
ComboBox2.DisplayMember = "Name";
ComboBox3.DataSource = _castBindingList.Where(x => x.MovieId == (Movie)ComboBox2.SelectedValue.Id).ToList();
ComboBox3.DisplayMember = "Names";
}
private void ComboBox2_SelectedValueChanged(object sender, EventArgs e)
{
ComboBox3.DataSource = _castBindingList.Where(x => x.MovieId == (Movie)ComboBox2.SelectedValue.Id).ToList();
ComboBox3.DisplayMember = "Names";
}
}

Composite join on two CSV files in C#

Starting from a table of daily fruit prices
fruits.csv
Day,Name,Kind,Price
2019-09-04,"apple","red",63.09
2019-09-04,"apple","yellow",52.14
2019-09-04,"orange","navel",41.18
2019-09-04,"orange","blood",41.18
2019-09-03,"apple","red",63.07
2019-09-03,"apple","yellow",52.11
2019-09-03,"orange","navel",41.13
2019-09-03,"orange","blood",41.13
I'd like to insert the reference prices by name and kind
fruit_ref_prices.csv
Name,Kind,Reference_Price
"apple","red",60.00
"apple","yellow",50.00
"orange","navel",40.00
"orange","blood",42.00
to result in the following table
Day,Name,Kind,Price,Reference_Price
2019-09-04,"apple","red",63.09,60.00
2019-09-04,"apple","yellow",52.14,50.00
2019-09-04,"orange","navel",41.18,40.00
2019-09-04,"orange","blood",41.18,42.00
2019-09-03,"apple","red",63.07,60.00
2019-09-03,"apple","yellow",52.11,50.00
2019-09-03,"orange","navel",41.13,40.00
2019-09-03,"orange","blood",41.13,42.00
The solution should be simple using C#'s built-in SQL-like syntax, and I'm sure the answer lies in one of the following tutorial pages:
Join clause
Perform custom join operations
Join by using composite keys
but I'm having a hard time identifying the syntax of this language.
In my attempt below instead of writing
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
I should be able to write
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
and fruit.kind equals fruit_ref.kind
but the Boolean expression is not accepted. Why?
My attempt is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
namespace MyConsoleApplication
{
class Program
{
const string root = #"c:\path\to\here\";
const string file1_in = root + #"fruits.csv";
const string file2_in = root + #"fruit_ref_prices.csv";
static void Main(string[] args)
{
Fruit_Basket fruit_basket = new Fruit_Basket(file1_in, file2_in);
fruit_basket.PrintFruits();
}
}
public class Fruit
{
public DateTime day { get; set; }
public string name { get; set; }
public string kind { get; set; }
public decimal price { get; set; }
public Fruit(DateTime newFruit_day,
string newFruit_name,
string newFruit_kind,
decimal newFruit_price)
{
this.day = newFruit_day;
this.name = newFruit_name;
this.kind = newFruit_kind;
this.price = newFruit_price;
}
}
public class Fruit_Ref
{
public string name;
public string kind;
public decimal reference_price;
public Fruit_Ref(string newName, string newKind, decimal newRef_Price)
{
this.name = newName;
this.kind = newKind;
this.reference_price = newRef_Price;
}
}
public class Fruit_Basket {
public List<Fruit> fruits { get; set; }
public List<Fruit_Ref> fruit_refs { get; set; }
public Fruit_Basket(string file1_in, string file2_in) {
build_fruit_list(file1_in);
build_fruit_ref_list(file2_in);
}
public void build_fruit_list(string file_in)
{
fruits = new List<Fruit>();
int count = 0;
StreamReader reader = new StreamReader(file_in);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
var newFruit_day = DateTime.Parse(splitLine[0]);
var newFruit_name = splitLine[1];
var newFruit_kind = splitLine[2];
var newFruit_price = decimal.Parse(splitLine[3]);
Fruit newFruit = new Fruit(newFruit_day,
newFruit_name,
newFruit_kind,
newFruit_price);
fruits.Add(newFruit);
}
}
reader.Close();
}
public void build_fruit_ref_list(string file_in)
{
fruit_refs = new List<Fruit_Ref>();
int count = 0;
StreamReader reader = new StreamReader(file_in);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
var newFruit_name = splitLine[0];
var newFruit_kind = splitLine[1];
var newFruit_ref_price = decimal.Parse(splitLine[2]);
Fruit_Ref newFruit_ref = new Fruit_Ref(newFruit_name,
newFruit_kind,
newFruit_ref_price);
fruit_refs.Add(newFruit_ref);
}
}
reader.Close();
}
public void PrintFruits()
{
var innerJoinQuery =
from fruit in fruits
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
select new { Day = fruit.day, Name = fruit.name, Kind = fruit.kind,
Price = fruit.price, Reference_Price = fruit_ref.reference_price };
Console.WriteLine($#"""Date"",""Name"",""Kind"",""Price"",""Ref Price""");
foreach (var i in innerJoinQuery)
{
Console.WriteLine($#"{i.Day},{i.Kind},{i.Price},{i.Reference_Price}");
}
}
}
}
You could also refactor your code to use the CsvHelper NuGet package for reading/writing CSV files.
First, You can make these classes to reflect the fruits, fruit references and final fruit structure.
public class Fruit
{
public string Day { get; set; }
public string Name { get; set; }
public string Kind { get; set; }
public string Price { get; set; }
}
public class FruitReferencePrice
{
public string Name { get; set; }
public string Kind { get; set; }
public string Reference_Price { get; set; }
}
public class FruitFinal
{
public string Day { get; set; }
public string Name { get; set; }
public string Kind { get; set; }
public string Price { get; set; }
public string ReferencePrice { get; set; }
public override string ToString()
{
return $"Day={Day},Name={Name},Kind={Kind},Price={Price},Reference_Price={ReferencePrice}";
}
}
Then you can make two methods to return the rows of each CSV file into List<Fruit> and List<FruitReferencePrice>.
private static IEnumerable<Fruit> BuildFruitList(string csvFilePath)
{
if (!File.Exists(csvFilePath))
{
throw new FileNotFoundException("Could not locate CSV at path " + csvFilePath, csvFilePath);
}
try
{
using var fileReader = File.OpenText(csvFilePath);
using var csv = new CsvReader(fileReader);
return csv.GetRecords<Fruit>().ToList();
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Enumerable.Empty<Fruit>();
}
}
private static IEnumerable<FruitReferencePrice> BuildFruitReferenceList(string csvFilePath)
{
if (!File.Exists(csvFilePath))
{
throw new FileNotFoundException("Could not locate CSV at path " + csvFilePath, csvFilePath);
}
try
{
using var fileReader = File.OpenText(csvFilePath);
using var csv = new CsvReader(fileReader);
return csv.GetRecords<FruitReferencePrice>().ToList();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Enumerable.Empty<FruitReferencePrice>();
}
}
Then you can perform a grouped join and output the merged result.
var path1 = "PATH\\fruits.csv";
var path2 = "PATH\\fruit_ref_prices.csv";
var fruitList = BuildFruitList(path1);
var fruitReferencePrices = BuildFruitReferenceList(path2);
var groupedJoin = from fruit in fruitList
join fruit_ref in fruitReferencePrices
on new { fruit.Name, fruit.Kind } equals new { fruit_ref.Name, fruit_ref.Kind }
select new FruitFinal
{
Day = fruit.Day,
Name = fruit.Name,
Kind = fruit.Kind,
Price = fruit.Price,
ReferencePrice = fruit_ref.Reference_Price
};
foreach (var fruit in groupedJoin)
{
Console.WriteLine(fruit.ToString());
}
Merged results:
Day=2019-09-04,Name=apple,Kind=red,Price=63.09,Reference_Price=60.00
Day=2019-09-04,Name=apple,Kind=yellow,Price=52.14,Reference_Price=50.00
Day=2019-09-04,Name=orange,Kind=navel,Price=41.18,Reference_Price=40.00
Day=2019-09-04,Name=orange,Kind=blood,Price=41.18,Reference_Price=42.00
Day=2019-09-03,Name=apple,Kind=red,Price=63.07,Reference_Price=60.00
Day=2019-09-03,Name=apple,Kind=yellow,Price=52.11,Reference_Price=50.00
Day=2019-09-03,Name=orange,Kind=navel,Price=41.13,Reference_Price=40.00
Day=2019-09-03,Name=orange,Kind=blood,Price=41.13,Reference_Price=42.00
Please change the equals clause as on new { fruit.name, fruit.kind } equals new { fruit_ref.name, fruit_ref.kind }
Why you require this
The query has two anonymous types (one for left table and one for right table). So to compare those anonymous types, the linq statement should use new keyword
Query :
var innerJoinQuery = from fruit in fruits
join fruit_ref in fruit_refs on new { fruit.name, fruit.kind } equals new { fruit_ref.name, fruit_ref.kind }
select new { Day = fruit.day, Name = fruit.name, Kind = fruit.kind,
Price = fruit.price, Reference_Price = fruit_ref.reference_price };

Allowing a user to select column headers to import

I'm using LINQtoCSV within a program that allows the user to import an order from a CSV file. I have all the code working however, if the CSV file doesn't have the exact column headers then it doesn't work.
Below is my class that LINQtoCSV reads into -
public class orderProduct
{
public orderProduct() { }
public string product { get; set; }
public string price { get; set; }
public string orderQty { get; set; }
public string value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(price) * Convert.ToDouble(orderQty)).ToString();
}
}
If the CSV file doesn't have the exact headers it won't work. The data I actually only need is the first 4 strings.
Below is my function that actually reads in the data.
private void csvParse()
{
// order.Clear();
string fileName = txt_filePath.Text.ToString().Trim();
try
{
CsvContext cc = new CsvContext();
CsvFileDescription inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
IEnumerable<orderProduct> fromCSV = cc.Read<orderProduct>(fileName, inputFileDescription);
foreach (var d in fromCSV)
{
MessageBox.Show($#"Product:{d.product},Quantity:""{d.orderQty}"",Price:""{d.price}""");
orderReturn.Add(d);
}
this.DialogResult = DialogResult.Yes;
this.Close();
}
catch (Exception ex)
{
if (ex.ToString().Contains("being used by another process"))
{
MessageBox.Show("Error: Please close the file in Excel and try again");
}
else
{
MessageBox.Show(ex.ToString());
}
}
}
I want the user to be able to just pass in a file and then select the relevant columns which relate to the corresponding values and then read in the data ignoring any columns that haven't been selected.
Hope this all makes sense, is something like this possible within LINQtoCSV
You have to add IgnoreUnknownColumns = true to your CsvFileDescription
CSV:
product,price,someColumn,orderQty,value,otherColumn
my product,$123,xx,2,$246,aa
my other product,$10,yy,3,$30,bb
Working code (I modified your code a little bit, to run it in a console)
using System;
using System.Collections.Generic;
using LINQtoCSV;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
csvParse();
Console.ReadLine();
}
private static void csvParse()
{
string fileName = "../../../test.csv"; // provide a valid path to the file
CsvContext cc = new CsvContext();
CsvFileDescription inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true,
IgnoreUnknownColumns = true // add this line
};
IEnumerable<orderProduct> fromCSV = cc.Read<orderProduct>(fileName, inputFileDescription);
foreach (var d in fromCSV)
{
Console.WriteLine($#"Product:{d.product},Quantity:""{d.orderQty}"",Price:""{d.price}""");
}
}
}
public class orderProduct
{
public orderProduct() { }
public string product { get; set; }
public string price { get; set; }
public string orderQty { get; set; }
public string value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(price) * Convert.ToDouble(orderQty)).ToString();
}
}
}
Output:
Product:my product,Quantity:"2",Price:"$123"
Product:my other product,Quantity:"3",Price:"$10"
If your properties have different names than CSV columns, you should use CsvColumn attribute:
public class OrderProduct
{
[CsvColumn(Name = "product")]
public string Product { get; set; }
[CsvColumn(Name = "price")]
public string Price { get; set; }
[CsvColumn(Name = "orderQty")]
public string OrderQuantity { get; set; }
public string Value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(Price) * Convert.ToDouble(OrderQuantity)).ToString();
}
}
Or if you prefer mapping columns by their indices:
public class OrderProduct
{
[CsvColumn(FieldIndex = 0)]
public string Product { get; set; }
[CsvColumn(FieldIndex = 1)]
public string Price { get; set; }
[CsvColumn(FieldIndex = 2)]
public string OrderQuantity { get; set; }
public string Value { get; set; }
public string calculateValue()
{
return (Convert.ToDouble(Price) * Convert.ToDouble(OrderQuantity)).ToString();
}
}
If you have to specify the columns on the fly, the only way seems to be to read raw data and process it yourself (the solution is based on this article):
internal class DataRow : List<DataRowItem>, IDataRow
{
}
...
int productColumnIndex = 0; // your users will provide it
var fromCSV = cc.Read<DataRow>(fileName);
foreach (var row in fromCSV)
{
var orderProduct = new OrderProduct
{
Product = row[productColumnIndex].Value,
};
Console.WriteLine(orderProduct.Product);
}

Get Collections from object using reflection and getCount (.net 4)

I have a requirement to reflect on a object get all properties that are collections and
1)GetCount for each collection
2)GetTotalCount (allCollectionCount)
3)Call a method with this collection.
Below is what I have done so far with a made up noddy structure for semplicity.
I am stuck in how to call this method and how to get count for collection.
Any suggestions?
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main()
{
var request = GetDataRequest();
//Get all properties
List<PropertyInfo> propInfoList =
new List<PropertyInfo>(request.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public));
//Get collections only
var myClassCollections=propInfoList.Where(xxx => xxx.PropertyType.GetInterfaces().Any(x => x == typeof (IEnumerable))).ToList();
var totalCountForAllCollections=????
foreach (var col in myClassCollections)
{
//How do I call my Method DoSomething
// DoSomething<?>(col.?????)
}
}
public void DoSomething<T>(List<T> objectCollection)
{
//etc...
}
private static DataRequest GetDataRequest()
{
DataRequest request = new DataRequest();
request.Addresses.Add(new Address
{
Id = 1,
City = "London",
Postcode = "32131",
Street = "London Road"
});
request.Addresses.Add(new Address
{
Id = 2,
City = "NewYork",
Postcode = "3432",
Street = "NewYork Road"
});
request.Customers.Add(new Customer
{
Id = 1,
Name = "Jo",
Surname = "Bloggs",
});
request.Customers.Add(new Customer
{
Id = 1,
Name = "Jon",
Surname = "Bloggs2",
});
request.Customers.Add(new Customer
{
Id = 1,
Name = "Jonny",
Surname = "Bloggs3",
});
return request;
}
}
public class DataRequest
{
public DataRequest()
{
Customers = new List<Customer>();
Orders = new List<Order>();
Addresses = new List<Address>();
}
public List<Customer> Customers { get; set; }
public List<Order> Orders { get; set; }
public List<Address> Addresses { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
public class Order
{
public int Id { get; set; }
public string Name { get; set; }
public string OrderNo { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
}
quick and dirty, here you go...
// ..
static class Program
{
static void Main()
{
var request = GetDataRequest();
//Get propertyValues for properties that are enumerable (i.e. lists,arrays etc)
var collectionProperties = request.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(propertInfo => propertInfo.PropertyType.GetInterfaces().Any(x => x == typeof(IEnumerable)))
.Select(p => p.GetValue(request, null))
.Cast<IEnumerable<object>>().ToList();
var totalCountForAllCollections = 0;
// iterate through the list of propertyValues
foreach (var collectionPropertyValue in collectionProperties)
{
totalCountForAllCollections += collectionPropertyValue.Count();
collectionPropertyValue.DoSomething();
}
System.Console.WriteLine("The total count for all collections is : {0}", totalCountForAllCollections);
System.Console.WriteLine("press any key to exit");
System.Console.ReadLine();
}
public static void DoSomething<T>(this IEnumerable<T> objectCollection)
{
//etc...
// N.B. you will have to use typeof(T) to implement logic specific to the type
// If the logic in this method is non-specific to the typeof(T) then Implement logic accordingly
System.Console.WriteLine("The type of the collection is: {0}", objectCollection.GetType());
System.Console.WriteLine("The count of items in this collection is:{0}", objectCollection.Count());
}
// ..
}
// ..

Categories