LINQ foreach statement to get items not working - c#

public partial class Form1 : Form
{
List<Doctor> doctors = new List<Doctor>();
List<Episode> episodes = new List<Episode>();
List<Companion> companions = new List<Companion>();
public Form1()
{
InitializeComponent();
doctorCombo.Enabled = false;
}
private void openMenu_Click(object sender, EventArgs e)
{
String input;
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
//Give a title to the popup and a filter for what files to open. Then opens the created dialogue
openFileDialog.Title = "Browse Text Files";
openFileDialog.Filter = "Text|*.txt"; ;
openFileDialog.ShowDialog();
//Read the contents of the file into a stream
var fileStream = openFileDialog.OpenFile();
//Opens a streamreader
using (StreamReader reader = new StreamReader(fileStream))
{
while ((input = reader.ReadLine()) != null)
{
String[] exploded = input.Split('|');
if (exploded[0].Contains("D "))
{
Doctor d = new Doctor(Convert.ToInt32(exploded[1]), exploded[2], Convert.ToInt32(exploded[3]), Convert.ToInt32(exploded[4]), exploded[5]);
doctors.Add(d);
}
if (exploded[0].Contains("E "))
{
Episode ep = new Episode(exploded[1], Convert.ToInt32(exploded[2]), Convert.ToInt32(exploded[3]), exploded[4]);
episodes.Add(ep);
}
if (exploded[0].Contains("C "))
{
Companion c = new Companion(exploded[1], exploded[2], Convert.ToInt32(exploded[3]), exploded[4]);
companions.Add(c);
}
}
reader.Close();
}
doctorCombo.Enabled = true;
doctorCombo.SelectedIndex = 0;
var doctorsCompanion =
from currentDoctor in doctors
join currentCompanion in companions on currentDoctor.debut equals currentCompanion.debut
select new {currentDoctor.actor, currentDoctor.series, currentDoctor.age, currentCompanion.debut, currentCompanion.name, currentCompanion.actor };
foreach (var item in doctorsCompanion)
{
outputList.Items.Add("");
}
The foreach statement at the end here keeps saying that is cannot operate on variables of type '?' because '?' does not contain a public instance definition for 'getenumerator'
I'm merging a class called doctor with a class called companion.

You have two actor members in your anonymous type:
select new {currentDoctor.actor, /* ... */ currentCompanion.actor };
This is an error, so the compiler cannot go on.
You must give them distinct names:
select new { doctor_actor = currentDoctor.actor, /* ... */
companion_actor = currentCompanion.actor };

Related

Comparing N Objects In A Complex Structure C#

I have a set of instances of the Data class that I want to compare.
Each instance has an unknown number of items in it's Files property.
I want to compare each instance of Data to the others and set FoundDifference to true if a version difference is found between two files with the same Name value.
Is there a simple algorithm to accomplish this?
Here is a sample setup of how the objects might look.
In this example you'd want everything except for f1, f21, and f31 to set the FoundDifference to true
class Data
{
public string DC { get; set; }
public List<File> Files { get; set; }
}
class File
{
public string Name { get; set; }
public string Version { get; set; }
public bool FoundDifference { get; set; }
}
class Program
{
static void Main(string[] args)
{
Data d1 = new Data();
d1.DC = "DC1";
File f1 = new File();
f1.Name = "File1";
f1.Version = "1";
d1.Files.Add(f1);
File f2 = new File();
f2.Name = "File2";
f2.Version = "1";
d1.Files.Add(f2);
File f3 = new File();
f3.Name = "File3";
f3.Version = "1";
d1.Files.Add(f3);
//Another
Data d2 = new Data();
d2.DC = "DC2";
File f21 = new File();
f21.Name = "File1";
f21.Version = "1";
d2.Files.Add(f21);
File f22 = new File();
f22.Name = "File2";
f22.Version = "2";
d2.Files.Add(f22);
File f23 = new File();
f23.Name = "File3";
f23.Version = "1";
d2.Files.Add(f23);
//Another
Data d3 = new Data();
d3.DC = "DC3";
File f31 = new File();
f31.Name = "File1";
f31.Version = "1";
d3.Files.Add(f31);
File f32 = new File();
f32.Name = "File2";
f32.Version = "2";
d3.Files.Add(f32);
File f33 = new File();
f33.Name = "File3";
f33.Version = "5";
d3.Files.Add(f33);
//How Can I change All Files FoundDifference prop to true if FileName is the same and a difference is in Version is found??
Console.ReadLine();
}
I'd handle that by using a Dictionary<string, List<File>> to keep track of the files from each Data like this. First iterate all the files in all the datas then lookup the file name in the dictionary and if not found create a new list and add it. Then check if that list has any files with a different version. If one is found set all the flags and finally add the file to the list.
public void SetDifferences(IEnumerable<Data> datas)
{
var fileLookup = new Dictionary<string, List<File>>();
foreach(var file in datas.SelectMany(d => d.Files))
{
if(!fileLookup.TryGetValue(file.Name, out var fileList))
{
fileList = new List<File>();
fileLookup.Add(file.Name, fileList);
}
if(fileList.Any(f => f.Version != file.Version))
{
foreach(var other in fileList)
{
other.FoundDifference = true;
}
file.FoundDifference = true;
}
fileList.Add(file);
}
}

Creating specific list of Items for each file in a Directory using C#

I am trying to create a List for every file available in a directory. Each file must have a separate list of items. I have created a list, but it shows all items available in all files. How to do it? Need help in this regard.
I am doing it like this, but it shows all Items "Product" in all files:
a piece of code is here:
List products = new List();
foreach (string file in fileList)
{
using (StreamReader sr = new StreamReader(file))
{
string line;
while ((line = sr.ReadLine()) != null)
{
rows++;
if (line.StartsWith("Product"))
{
var val = line.Split(',');
upc = val[1];
pName = val[3];
products.Add(upc);
products.Add(pName);
productList = string.Join(",", products.ToArray());
Add these clases
public class ProductFile
{
public string Name;
public List<Product> Products = new List<Product>();
}
public class Product
{
public string Upc;
public string PName;
}
And then, in your code:
var productFiles = new List<ProductFile>();
foreach (string file in fileList)
{
var productFile = new ProductFile(){ Name = Path.GetFileName(file) };
using (StreamReader sr = new StreamReader(file))
{
string line;
while ((line = sr.ReadLine()) != null)
{
rows++;
if (line.StartsWith("Product"))
{
var val = line.Split(',');
var product = new Product()
{
Upc = val[1],
PName = val[3]
};
productFile.Products.Add(product); //Adds a product
}
}
};
productFiles.Add(productFile); //Adds a file
}

Accessing string from an html template class

I'm trying to access a certain variable from another class, however I'm not able to do so. I have two buttons - the first button sets token to an html template file. The second should generate the file. The first button calls the class. The second button should call the string from the class for generation.
My first button is as follows:
private void btnTemplate_Click(object sender, EventArgs e)
{
if ((txtTitle.Text == "") && (txtSku.Text == "") && (txtPrice.Text == "") && (txtDesc.Text == "") && (txtImg.Text == ""))
{
MessageBox.Show("No row selected", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
OpenFileDialog SetData = new OpenFileDialog();
SetData.Filter = "HTML|*.html;";
GlobalVar.setPath = "C:\\genHtml.html";
var result = SetData.ShowDialog();
DataSet ds = new DataSet();
if (result == DialogResult.OK)
{
string fileName = SetData.FileName;
var template = new HtmlTemplate(#SetData.FileName);
var output = template.Render(new
{
TITLE = txtTitle.Text,
SKU = txtSku.Text,
PRICE = txtPrice.Text,
DESC = txtDesc.Text,
IMAGE = txtImg.Text
});
File.WriteAllText(#GlobalVar.setPath, output);
}
}
}
My second button:
private void btnGenerate_Click(object sender, EventArgs e)
{
try
{
System.Diagnostics.Process.Start(GlobalVar.setPath);
}
catch
{
MessageBox.Show("Please select a template first", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
I get an error with the string output. How can I call string 'output' for the second button?
My class is as follows:
class HtmlTemplate
{
private string _html;
public HtmlTemplate(string templatePath)
{
using (var reader = new StreamReader(templatePath))
_html = reader.ReadToEnd();
}
public string Render(object values)
{
string output = _html;
foreach (var p in values.GetType().GetProperties())
output = output.Replace("[" + p.Name + "]", (p.GetValue(values, null) as string) ?? string.Empty);
return output;
}
}
There is not enough context to be sure, but in the case of the second button it looks like output is not declared as a variable or possibly not assigned a (correct) value.
File.WriteAllText(#GlobalVar.setPath, output);
could become
var template = new HtmlTemplate(#SetData.FileName);
File.WriteAllText(#GlobalVar.setPath, template.Render(new
{
TITLE = txtTitle.Text,
SKU = txtSku.Text,
PRICE = txtPrice.Text,
DESC = txtDesc.Text,
IMAGE = txtImg.Text
});
or alternatively
var template = new HtmlTemplate(#SetData.FileName);
var output = template.Render(new
{
TITLE = txtTitle.Text,
SKU = txtSku.Text,
PRICE = txtPrice.Text,
DESC = txtDesc.Text,
IMAGE = txtImg.Text
});
File.WriteAllText(#GlobalVar.setPath, output);

How can I pass a function with one parameter to an ICommand?

Here's my ICommand:
public ICommand ConfirmLotSavedCommand {
get
{
return new RelayCommand(ConfirmLotSaved);
}
}
The problem is I have deserialized data that I want to store into database after a user clicks confirm button. If the user does not click on confirm or the lot number already exists, then I don't want to save the deserialized string in db.
I had trouble calling a function with one parameter inside my ConfirmLotSaved() method because of scope.
So I created a set the deserialized lot as a field and put the code to save to db inside of ConfirmLotSaved(). However, the field is null for some strange reason... I'm not sure why.
Here's my attempt:
private LotInformation lot; //field that is supposed to contain all the deserialized info
private void ConfirmLotSaved()
{
using (var db = new DDataContext())
{
bool lotNumDbExists = db.LotInformation.Any(r => r.lot_number == DeserialLotNumber);
if (lotNumDbExists == false)
{
successWindow.Message = "Successfully Saved Lot";
dialogService.ShowDialog(successWindow.Message, successWindow);
LotInformation newLot = new LotInformation();
if (newLot != null)
{
newLot.Id = lot.Id;
newLot.lot_number = lot.lot_number;
newLot.exp_date = lot.exp_date;
LotNumber = Lot.lot_number;
ExpirationDate = Lot.exp_date.ToString();
foreach (Components comp in lot.Components)
{
newLot.Components.Add(comp);
}
ComponentsList = newLot.Components;
foreach (Families fam in lot.Families)
{
newLot.Families.Add(fam);
}
FamiliesList = newLot.Families;
try
{
db.LotInformation.Add(newLot);
db.SaveChanges();
//Grabs the lot_number column from db that is distinct
var lotNum = db.LotInformation.GroupBy(i => i.lot_number).Select(group => group.FirstOrDefault());
//Loops through the lot numbers column in db and converts to list
foreach (var item in lotNum)
{
Console.WriteLine(item.lot_number);
}
LotNumList = lotNum.ToList();
Console.WriteLine("successfully");
}
catch
{
//TODO: Add a Dialog Here
}
}
else if (lotNumDbExists == true)
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}
}
Deserialization function to see where lot is grabbing data:
public void DeserializedStream(string filePath)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "lot_information";
xRoot.IsNullable = false;
// Create an instance of lotinformation class.
LotInformation lot = new LotInformation();
// Create an instance of stream writer.
TextReader txtReader = new StreamReader(filePath);
// Create and instance of XmlSerializer class.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(LotInformation), xRoot);
// DeSerialize from the StreamReader
lot = (LotInformation)xmlSerializer.Deserialize(txtReader);
// Close the stream reader
txtReader.Close();
LotInformation newList = new LotInformation();
using (var db = new DDataContext())
{
bool isDuplicate = db.LotInformation.Any(r => r.lot_number == lot.lot_number);
if (newList != null && isDuplicate == false)
{
newList.Id = lot.Id;
newList.lot_number = lot.lot_number;
newList.exp_date = lot.exp_date;
DeserialLotNumber = newList.lot_number;
DeserialExpirationDate = newList.exp_date.ToString();
foreach (Component comp in lot.Components)
{
newList.Components.Add(comp);
}
DeserialComponentsList = newList.Components;
foreach (Families fam in lot.Families)
{
newList.Families.Add(fam);
}
DeserialFamiliesList = newList.Families;
}
else if (isDuplicate == true)
{
DeserialAnalytesList = null;
DeserialFamiliesList = null;
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}
I figured out what was wrong:
After setting private LotInformation lot; field before constructor, I redeclared locally my mistake:
LotInformation lot = new LotInformation();
Changed it to:
lot = new LotInformation();
and it works.
I suggest you to use RelayCommand's generic edition http://www.kellydun.com/wpf-relaycommand-with-parameter/
It will allow you to pass lot to your command from view, all you need to store lot in current DataContext.

Read File and display contents

I want that when I click the button "List all Customers", the code should read the Customer.csv file and display the information on the form called "List All Customers".
How can I do that?
public static void ReadFile()
{
StreamReader sr = File.OpenText("Customer.csv");
}
public static void LoadCustomers()
{
try
{
if (File.Exists("Customer.csv"))
{
string temp = null;
int count = 0;
using (StreamReader sr = File.OpenText(#"Customer.csv"))
{
while ((temp = sr.ReadLine()) != null)
{
temp = temp.Trim();
string[] lineHolder = temp.Split(',');
Customer tempCust = new Customer();
tempCust.customerName = lineHolder[0];
tempCust.customerAddress = lineHolder[1];
tempCust.customerZip = Convert.ToInt32(lineHolder[2]);
myCustArray[count] = tempCust;
count++;
}//end for loop
}
}
else
{
File.Create("Customer.csv");
}
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show("File Loading Error: " + e.Message);
}
}
I'm not sure what kind of control you want to display this data in but your method could just return a list of Customer, then you can add to a ListBox, ListView or DataGrid
public static IEnumerable<Customer> LoadCustomers(string filename)
{
if (File.Exists(filename))
{
foreach (var line in File.ReadAllLines(filename).Where(l => l.Contains(',')))
{
var splitLine = line.Split(',');
if (splitLine.Count() >= 3)
{
yield return new Customer
{
customerName = splitLine[0].Trim(),
customerAddress = splitLine[1].Trim(),
customerZip = Convert.ToInt32(splitLine[2].Trim())
};
}
}
}
}
ListBox
listBox1.DisplayMember = "customerName";
listBox1.Items.AddRange(LoadCustomers(#"G:\Customers.csv").ToArray());
First, take advantage of the list object:
public static void ReadFile()
{
StreamReader sr = File.OpenText("Customer.csv");
}
public static void LoadCustomers()
{
try
{
if (File.Exists("Customer.csv"))
{
string temp = null;
var retList = new List<Customer>();
using (StreamReader sr = File.OpenText(#"Customer.csv"))
{
while ((temp = sr.ReadLine()) != null)
{
temp = temp.Trim();
string[] lineHolder = temp.Split(',');
retlist.add(new Customer(){
customerName = linerHolder[0],
customerAddress = lineHolder[1],
customerZip = Convert.ToInt32(lineHolder[2])
});
}//end for loop
}
}
else
{
File.Create("Customer.csv");
}
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show("File Loading Error: " + e.Message);
}
}
just wrap it up in a class, call if from the controller and populate up the results. Depending on how often you will be updating this data, you might look into caching it, so you don't have to run this process every X seconds for each user.

Categories