Overlapping printing of an IEnumerable collection - c#

I'm reading the contents of an XML file into an IEnumerable collection (array) and I need to print each iteration (block of like XML data) on a separate page.
I'm using the Print() function and e.HasMorePages. My problem is that the foreach loops through all iterations of the IEnumerable collection for each print so I'm printing the correct number of pages but each page contains all iterations on top of each other instead of one per page. Can anyone give me an idea for a solution or a better way to manage this process?
Here's the pertinent portion of the code...
// Print Employee General info
foreach (EmployeeInfo itm in GEmployeeXGD.GetEmployeeGeneralData())
{
try
{
empFirstName = itm.FirstName;
empLastName = itm.LastName;
empMidInitial = itm.MidInitial;
etc…
// Set field coordinates for each employee
// ******* Employee's general information ********
PointF empFirstNameLoc = new PointF(430, 271);
PointF empLastNameLoc = new PointF(600, 271);
PointF empMidInitialLoc = new PointF(563, 271);
etc…
// Send field text data
using (Font courierFont = new Font("Courier", 10, FontStyle.Bold))
{
e.Graphics.DrawString(empFirstName, courierFont, Brushes.Black, empFirstNameLoc);
e.Graphics.DrawString(empLastName, courierFont, Brushes.Black, empLastNameLoc);
e.Graphics.DrawString(empMidInitial, courierFont, Brushes.Black, empMidInitialLoc);
etc…
}
}
catch (Exception error) { MessageBox.Show(error.ToString()); }
e.HasMorePages = (records < Globals.totalRecordCount);
}
That's helpful Joel, thanks.
GEmployeeXGD reverences a class with a single method. The method reads in the XML data that I need and populates the IEnumerable<> collection as an array. Here's the method..
public Array GetEmployeeGeneralData()
{
// XML source file
var xmlEmployeeFile = File.ReadAllText("Corrections.xml");
XDocument employeeDoc = XDocument.Parse(xmlEmployeeFile);
XElement w2cEmployeeDat = employeeDoc.Element("CorrectedDAta");
EmployeeInfo[] employeeGenInfo = null;
if (w2cEmployeeDat != null)
{
IEnumerable<XElement> employeeRecords = w2cEmployeeDat.Elements("Employee");
try
{
employeeGenInfo = (from itm in employeeRecords
select new EmployeeInfo()
{
FirstName = (itm.Element("FirstName") != null) ? itm.Element("FirstName").Value : string.Empty,
LastName = (itm.Element("LastName") != null) ? itm.Element("LastName").Value : string.Empty,
MidInitial = (itm.Element("MidInitial") != null) ? itm.Element("MidInitial").Value : string.Empty,
etc…
}).ToArray<EmployeeInfo>();
}
catch (Exception) { MessageBox.Show(error.ToString()); }
}
Globals.SetRecordCount(employeeGenInfo.Count<EmployeeInfo>());
recordCount = Globals.totalRecordCount;
return employeeGenInfo;
}

You need to keep track of what page you are on, and then do something like this:
var items = GEmployeeXGD.GetEmployeeGeneralData().Skip( (pageNumber-1) * numberItemsPerPage).Take(numberOfItemsPerPage);
foreach (EmployeeInfo itm in items)
{
//...
}
Even better if you can avoid calling that same method over and over, and keep the same IEnumerable across individual pages. But to demonstrate this, I'd need to see more of the context of the class where this method lives, and know more about how GEmployeeXGD works.

Related

Is there a way to include null values in a list in C#?

I have a text file filled with several lines of data, and I would like to split it into 5 different elements like so..
I am successfully reading in the data and putting it into an array. Now I would like to split each part of the text up into different lists so I can compare the data against one another.
I have currently managed to read in the first 4 elements of each line into their relevant lists but the 5th one is throwing me the error "System.IndexOutOfRangeException" which I can only assume is because the first line it reads in has no value for the 5th element?
So my question is, is there a way to populate null values when writing them to a number of lists?
I've tried manually assigning the size of the array and lists but I still get the same error.
Here is my code:
class Program
{
static void Main(string[] args)
{
// Reading in file containing data from BT Code Evaluation sheet (for testing purposes).
// Each line gets stored into a string array, each element is one line of the data.txt file.
//String[] lines = System.IO.File.ReadAllLines(#"C:\Users\Ad\Desktop\data.txt");
String[] lines = new String[5] {"monitorTime", "localTime", "actor", "action", "actor2"};
lines = System.IO.File.ReadAllLines(#"C:\Users\Ad\Desktop\data.txt");
char delimiter = ' ';
List<String> monitorTime = new List<String>();
List<String> localTime = new List<String>();
List<String> actor = new List<String>();
List<String> action = new List<String>();
List<String> actor2 = new List<String>();
// Foreach loop displays the lines of text in the data file.
foreach (String line in lines)
{
// Writes the data to the console.
Console.WriteLine(line);
String[] data = new String[5] { "monitorTime", "localTime", "actor", "action", "actor2" };
data = line.Split(delimiter);
monitorTime.Add(data[0]);
localTime.Add(data[1]);
actor.Add(data[2]);
action.Add(data[3]);
actor2.Add(data[4]);
}
foreach (String time in monitorTime)
{
Console.WriteLine(time);
}
foreach (String time in localTime)
{
Console.WriteLine(time);
}
foreach (String name in actor)
{
Console.WriteLine(name);
}
foreach (String actions in action)
{
Console.WriteLine(actions);
}
foreach (String name in actor2)
{
if (name != null)
{
Console.WriteLine("UNKNOWN");
}
else
{
Console.WriteLine(actor2);
}
}
// Creates an empty line between the data and the following text.
Console.WriteLine("");
// Displays message in console.
Console.WriteLine("Press any key to analyse data and create report...");
Console.ReadKey();
}
}
You need to check the bounds of you array before you try to add. If their aren't enough items you can add null instead.
For example:
actor2.Add(data.length > 4 ? data[4] : null)
(Note: You could do the same type of check on the other items as well, unless you are positive that the last item is the only one that might be null)
This is using the ternary operator, but you could also use a simple if/else, but it'll be more verbose. It's equivalent to:
if (data.length > 4)
{
actor2.Add(data[4]);
}
else
{
actor2.Add(null);
}
This along with Console.WriteLine(name); instead of Console.WriteLine(actor2); should fix you immediate problem.
However, a much better design here would be to have a single list of objects with MonitorTime, LocalTime, Actor, Action and Actor2 properties. That way you don't ever have to worry that the 5 parallel arrays might get out of sync.
For example, create a class like this:
public class DataItem
{
public string MonitorTime { get; set; }
public string LocalTime { get; set; }
public string Actor { get; set; }
public string Action { get; set; }
public string Actor2 { get; set; }
}
Then instead of your 5 List<String>, you have one List<DataItem>:
List<DataItem> dataList = new List<DataItem>();
Then in your loop to populate it you'd do something like:
data = line.Split(delimiter);
dataList.Add(new DataItem()
{
MonitorTime = data[0],
LocalTime = data[1],
Actor = data[2],
Action = data[3],
Actor2 = data.length > 4 ? data[4] : null
});
Then you can access them later with something like:
foreach (var item in dataList)
{
Console.WriteLine(item.MonitorTime);
//...
}
In your for each you should be checking to see if the index exists before populating the object.
foreach (String line in lines)
{
// Writes the data to the console.
Console.WriteLine(line);
String[] data = new String[5] { "monitorTime", "localTime", "actor", "action", "actor2" };
data = line.Split(delimiter);
monitorTime.Add(data[0]);
localTime.Add(data[1]);
actor.Add(data[2]);
action.Add(data[3]);
if (data.Length > 4) {
actor2.Add(data[4]);
}
}
There's better ways to do this but this is a simple solution for now.

how to stop looping in file in c#?

I'm Trying to save a customer in file but when i'm saving it, it keeps looping the person i entered i do not know where i'm doing it wrong .The problem is i'm thinking my logic is good but i know i'm doing something wrong somewhere which i can not find it .If you could help me i really appreciate it.
public partial class admin : Window
{
accounts acclist = new accounts();
customers cuslist = new customers();
public admin(string pin, accounts myacc, customers mycus)
{
InitializeComponent();
acclist = myacc;
cuslist = mycus;
}
public void saveaccount()
{
using (StreamWriter writer = new StreamWriter("account.txt"))
{
for (int i = 0; i < acclist.Count; i++)
{
var info = new List<string>
{
acclist[i].accounttype.ToString(),
acclist[i].PIN,
acclist[i].accountnumber,
acclist[i].accountbalance.ToString()
};
var account = String.Join(";", info);
writer.WriteLine(account);
}
}
}
//save to customer file
public void savefile()
{
using (StreamWriter writer = new StreamWriter("customer.txt"))
{
for (int i = 0; i < cuslist.Count; i++)
{
var info = new List<string>
{
cuslist[i].NAME.ToString(),
cuslist[i].pin,
};
var customer = String.Join(";", info);
writer.WriteLine(customer);
}
}
}
// add user
private void Sub_Click(object sender, RoutedEventArgs e)
{
customer newCus = new customer();
account newAcc= new account();
try
{
newCus.NAME = Nameadd.Text;
newCus.pin = pinadd.Text;
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'C';
for (int i = 0; i < acclist.Count; i++)
{
{
if(newAcc.accounttype == 'C')
{
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'S';
}
}
cuslist.add(newCus);
acclist.add(newAcc);
savefile();
saveaccount();
}
}
catch(Exception error)
{
MessageBox.Show(error.Message);
}
}
}
In your "save" event, your doing a for loop over the "count" of elements in your account list. That account list variable is global in scope to your class. The problem is that in your loop, you're adding to that list... so really the list you're iterating over is mutating right under-neath you. As you add that customer, the loop starts next iteration, checks the "count", and ends only when "i" is equal in value to the count. However each pass you're adding to the count... so technically you'll never reach the end as the accList.Count is constantly increasing by 1 each pass.
private void Sub_Click(object sender, RoutedEventArgs e)
{
customer newCus = new customer();
account newAcc= new account();
try
{
newCus.NAME = Nameadd.Text;
newCus.pin = pinadd.Text;
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'C';
for (int i = 0; i < acclist.Count; i++) // here you are checking acclist.Count... each iteration this increases by 1, thus i will never technically ever be equivalent to acclist.Count
{
{
if(newAcc.accounttype == 'C')
{
newAcc.PIN = pinadd.Text;
newAcc.accountnumber = Accountnumadd.Text;
newAcc.accounttype = 'S';
}
}
cuslist.add(newCus);
acclist.add(newAcc); // <-- this is where you're adding to acclist each time. However inside this loop you're constantly increasing its size... thus your infinite loop you're hitting.
savefile();
saveaccount();
}
}
catch(Exception error)
{
MessageBox.Show(error.Message);
}
}
I suggest for one, you use a ForEach statement as it's been suggested. Also, use a separate list to hold your "new" accounts. If you need to add to acclist for whatever reason, then after you've added to your new list, iterate over that and add each one of those back to your acclist. This way you avoid mutating the very object you're looping over.
Another way is to first store the "count" into a variable and check against that so it never changes.
var myCounter = acclist.Count
for (int i = 0; i < myCounter ; i++)
{
...
But, I don't know which option is best for you as I obviously don't know the larger context of what it is you need to ultimately do. :) Either solution should stop your infinite loop though.
Consider the use of a foreach loop. This loop is best used for applications like the one in your savefile() method. It's great for applications where you want to perform an action for every item in a list or collection.
See here: https://msdn.microsoft.com/en-us/library/ttw7t8t6.aspx?f=255&MSPPError=-2147217396
EDIT: The requested example:
List<string> FruitBasket = new List<string>();
FruitBasket.Add("apple");
FruitBasket.Add("banana");
FruitBasket.Add("orange");
foreach (string fruit in FruitBasket)
{
Console.WriteLine(fruit);
}
and this results in an output of:
apple, banana, orange.
In the foreach loop you need to declare a variable of the same type as your collection (so in this example I had a List of strings so I made my variable a string, and then they keyword in assigns this variable to each item in that collection, one at a time, starting from the beginning.

Creating a two-dimensional array

I am trying to create a two dimensional array and I am getting so confused. I was told by a coworker that I need to create a dictionary within a dictionary for the array list but he couldn't stick around to help me.
I have been able to create the first array that lists the the programs like this
+ project 1
+ project 2
+ project 3
+ project 4
The code that accomplishes this task is below-
var PGList = from x in db.month_mapping
where x.PG_SUB_PROGRAM == SP
select x;
//select x.PG.Distinct().ToArray();
var PGRow = PGList.Select(x => new { x.PG }).Distinct().ToArray();
So that takes care of my vertical array and now I need to add my horizontal array so that I can see the total amount spent in each accounting period. So the final output would look like this but without the dashes of course.
+ program 1-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 2-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 3-------100---200---300---400---500---600---700---800---900---1000---1100---1200
+ program 4-------100---200---300---400---500---600---700---800---900---1000---1100---1200
I have tried to use a foreach to cycle through the accounting periods but it doesn't work. I think I might be on the right track and I was hoping SO could provide some guidance or at the very least a tutorial for me to follow. I have posted the code that I written so far on the second array below. I am using C# and MVC 3. You might notice that their is no dictionary within a dictionary. If my coworker is correct how would I do something like that, I took a look at this question using dictionary as a key in other dictionary but I don't understand how I would use it in this situation.
Dictionary<string, double[]> MonthRow = new Dictionary<string, double[]>();
double[] PGContent = new double[12];
string lastPG = null;
foreach (var item in PGRow)
{
if (lastPG != item.PG)
{
PGContent = new double[12];
}
var MonthList = from x in db.Month_Web
where x.PG == PG
group x by new { x.ACCOUNTING_PERIOD, x.PG, x.Amount } into pggroup
select new { accounting_period = pggroup.Key.ACCOUNTING_PERIOD, amount = pggroup.Sum(x => x.Amount) };
foreach (var P in MonthList)
{
int accounting_period = int.Parse(P.accounting_period) - 1;
PAContent[accounting_period] = (double)P.amount;
MonthRow[item.PG] = PGContent;
lastPG = item.PG;
}
I hope I have clearly explained my issue, please feel free to ask for any clarification needed as I need to solve this problem and will be checking back often. Thanks for your help!
hope this helps.
// sample data
var data = new Dictionary<string, List<int>>();
data.Add("program-1", new List<int>() { 100, 110, 130 });
data.Add("program-2", new List<int>() { 200, 210, 230 });
data.Add("brogram-3", new List<int>() { 300, 310, 330 });
// query data
var newData = (from x in data
where x.Key.Contains("pro")
select x).ToDictionary(v => v.Key, v=>v.Value);
// display selected data
foreach (var kv in newData)
{
Console.Write(kv.Key);
foreach (var val in kv.Value)
{
Console.Write(" ");
Console.Write(val.ToString());
}
Console.WriteLine();
}
output is:
program-1 100 110 130
program-2 200 210 230
Don't try to use anonymous types or LINQ projection to create new data types, especially if you're a beginner, you will just get confused. If you want a specialized data type, define one; e.g.:
public class Account
{
public string Name { get; private set; }
public decimal[] MonthAmount { get; private set; }
readonly int maxMonths = 12;
public Account(string name, ICollection<decimal> monthAmounts)
{
if (name == null)
throw new ArgumentNullException("name");
if (monthAmounts == null)
throw new ArgumentNullException("monthAmounts");
if (monthAmounts.Count > maxMonths)
throw new ArgumentOutOfRangeException(string.Format(" monthAmounts must be <= {0}", maxMonths));
this.Name = name;
this.MonthAmount = new decimal[maxMonths];
int i = 0;
foreach (decimal d in monthAmounts)
{
this.MonthAmount[i] = d;
i++;
}
}
}
Use instances of this type directly, you do not have to convert them to arrays, dictionaries, lists, or anything else:
var accountPeriods = new List<Account>();
accountPeriods.Add(new Account("program-1", new decimal[] { 1, 2, 3, 4 }));
You can use LINQ or whatever to query or alter instances of your new type:
foreach (Account a in accountPeriods)
foreach (decimal d in a.MonthAmount)
DoSomethingWith(d);
That should be enough to get you started.
I want to thank #Ray Cheng and #Dour High Arch for their help but I have figured out another way to accomplish this task and I wanted to post my code so that the next person that is having the same trouble can figure out their problem faster.
Above I split my code into more managable sections to explain my problem as clearly as I could and the code below has all those parts combined so you can see the big picture. This code returns an array that contains the program and the amounts for every month.
public virtual ActionResult getAjaxPGs(string SP = null)
{
if (SP != null)
{
var PGList = from x in db.month_mapping
where x.PG_SUB_PROGRAM == SP
select x;
var PGRow = PGList.Select(x => new { x.PG }).Distinct().ToArray();
float[] PGContent = new float[12];
Dictionary<string,float[]> MonthRow = new Dictionary<string, float[]>();
foreach (var item in PGRow)
{
PGContent = new float[12];
var MonthList = from x in db.month_Web
where x.PG == item.PG
group x by new { x.ACCOUNTING_PERIOD, x.PG, x.Amount } into pggroup
select new { accounting_period = pggroup.Key.ACCOUNTING_PERIOD, amount = pggroup.Sum(x => x.Amount) };
foreach (var mon in MonthList)
{
int accounting_period = int.Parse(mon.accounting_period) - 1;
PGContent[accounting_period] = (float)mon.amount/1000000;
}
MonthRow[item.PG] = PGContent;
}
return Json(MonthRow, JsonRequestBehavior.AllowGet);
}
return View();
}
This code worked great for me since I am pulling from a Linq to SQL query instead of adding data directly into the code. My problems stemmed from mainly putting the data pulls outside of the foreach loops so it only pulled 1 piece of data from the SQL instead of all twelve months. I hope this helps some one else who is trying to pull data in from SQL data sources into multidimensional arrays.

MVCCrud Using LinqToEntities

There is a sample application called MVCCrud. This example is quite good and I would like to use it as the framework on a project that I am working on.
The problem is that MVCCrud uses LingToSQL and I would like to use LinqToEntities. I got most everything to work correctly once I converted over to LinqToEntities except one place.
In the following code on the lines i = typeof(TModel).GetProperty(primaryKey).GetValue(p, null),
cell = getCells(p)
it gives a Linq to Entities does not recognize GetValue.
Can someone help me refactor the following code?
items = items.OrderBy(string.Format("{0} {1}", sidx, sord)).Skip(pageIndex * pageSize).Take(pageSize).AsQueryable();
// Generate JSON
var jsonData =
new
{
total = totalPages,
page,
records = totalRecords,
rows = items.Select(
p => new
{
// id column from repository
i = typeof(TModel).GetProperty(primaryKey).GetValue(p, null),
cell = getCells(p)
}).ToArray()
};
return Json(jsonData);
and here is the getCell method:
private string[] getCells(TModel p)
{
List<string> result = new List<string>();
string a = actionCell(p);
if (a != null)
{
result.Add(a);
}
foreach (string column in data_rows.Select(r => r.value))
{
try
{
// hack for tblcategory.name
string[] parts = column.Split('.');
// Set first part
PropertyInfo c = typeof(TModel).GetProperty(parts[0]);
object tmp = c.GetValue(p, null);
// loop through if there is more than one depth to the . eg tblCategory.name
for (int j = 1; j < parts.Length; j++)
{
c = tmp.GetType().GetProperty(parts[j]);
tmp = c.GetValue(tmp, null);
}
if (tmp.GetType() == typeof(DateTime))
{
result.Add(((DateTime)tmp).ToString(dateTimeFormat));
}
else if (tmp.GetType() == typeof(float))
{
result.Add(((float)tmp).ToString(decimalFormat));
}
else if (tmp.GetType() == typeof(double))
{
result.Add(((double)tmp).ToString(decimalFormat));
}
else if (tmp.GetType() == typeof(decimal))
{
result.Add(((decimal)tmp).ToString(decimalFormat));
}
else
{
result.Add(tmp.ToString());
}
}
catch (Exception)
{
result.Add(string.Empty);
}
}
return result.ToArray();
}
Do this ToList() instead of AsQueryable():
items = items.OrderBy(string.Format("{0} {1}", sidx, sord)).Skip(pageIndex * pageSize).Take(pageSize).ToList();
You can't execute any external method "within" linq query.
And may you say that was working in Linq2Sql then you should know when you call any external method "Like ToString()" Linq2Sql will fetch all data from database then handle your query in the memory and that maybe a serious harming if you have a lot of records.
For more information look at this

C#. Set a member object value using reflection

I need your help with the following code below. Basically I have a class called "Job" which has some public fields. I'm passing to my method "ApplyFilter" two parameters "job_in" and "job_filters". First parameter contains actual data, and the second one has instructions (if any). I need to iterate through "job_in" object, read it's data, apply any instructions by reading "job_filters", modify data (if needed) and return it in a new "job_out" object. Everything works fine till i need to store my data in "job_out" object:
public class Job
{
public string job_id = "";
public string description = "";
public string address = "";
public string details = "";
}
...
private Job ApplyFilters(Job job_in, Job job_filters)
{
Type type = typeof(Job);
Job job_out = new Job();
FieldInfo[] fields = type.GetFields();
// iterate through all fields of Job class
for (int i = 0; i < fields.Length; i++)
{
List<string> filterslist = new List<string>();
string filters = (string)fields[i].GetValue(job_filters);
// if job_filters contaisn magic word "$" for the field, then do something with a field, otherwise just copy it to job_out object
if (filters.Contains("$"))
{
filters = filters.Substring(filters.IndexOf("$") + 1, filters.Length - filters.IndexOf("$") - 1);
// MessageBox.Show(filters);
// do sothing useful...
}
else
{
// this is my current field value
var str_value = fields[i].GetValue(job_in);
// this is my current filed name
var field_name = fields[i].Name;
// I got stuck here :(
// I need to save (copy) data "str_value" from job_in.field_name to job_out.field_name
// HELP!!!
}
}
return job_out;
}
Please help. I've seen a few samples by using properties, but i'm pretty sure it is possible to do the same with fields as well. Thanks!
Try this
public static void MapAllFields(object source, object dst)
{
System.Reflection.FieldInfo[] ps = source.GetType().GetFields();
foreach (var item in ps)
{
var o = item.GetValue(source);
var p = dst.GetType().GetField(item.Name);
if (p != null)
{
Type t = Nullable.GetUnderlyingType(p.FieldType) ?? p.FieldType;
object safeValue = (o == null) ? null : Convert.ChangeType(o, t);
p.SetValue(dst, safeValue);
}
}
}
fields[i].SetValue(job_out, str_value);

Categories