Reputation: 101
I have attempted this over and over, but I'm now just going in circles.
I have a set of mostly unique job files stored externally, from these files I'm collection information and storing it in my program via a List.
On occasion, I have two job files named the same (this unfortunately cant be avoided) but containing different data (the most unique of which is a DateTime string).
My program works by polling 3 locations for these files (all locations are mirrored so the same data is present in each), collecting the information then outputting the info to a ListView window dependent on current status of each job file.
The issue I'm having is preventing duplicate entries being added to my List resulting in 69 objects in the list for only 23 jobs files.
To avoid having duplicates output in the listview, I attempted to use a dictionary to store known keys based on the job file name, however this falls over when reaching the second file that is named the same as an existing entry. Resulting in me only seeing 21 jobs in a list that should contain 23 (I also get an exception thrown). I got around this by appending something to the end of the second entry, but this isn't ideal, and was found to cause other complications later on.
Any ideas?
Here's what I have so far.
public List<object> PendingJobsArray = new List<object>();
public List<object> ActiveJobsArray = new List<object>();
public List<object> CompletedJobsArray = new List<object>();
public JobObject PendingJobs = new JobObject();
public JobObject ActiveJobs = new JobObject();
public JobObject CompletedJobs = new JobObject();
private void updatePending(int index, XmlParser xmlParser)
{
if (Program.PendingJobsArray.Count == 0)
{
Program.KnownPendingJobs.Add(xmlParser.getElementAttrValue(index, "Name"), xmlParser.getElementAttrValue(index, "DateTime"));
JobObject PendingJobs = new JobObject();
PendingJobs.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
PendingJobs.jobObjectState = xmlParser.getElementAttrValue(index, "State");
PendingJobs.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
PendingJobs.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
PendingJobs.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
PendingJobs.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
PendingJobs.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
PendingJobs.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
PendingJobs.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
// PendingJobs.jobObjectServer = xmlParser.getElementAttrValue("Server");
// PendingJobs.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
Program.PendingJobsArray.Add(PendingJobs);
}
else if (Program.KnownPendingJobs.ContainsKey(xmlParser.getElementAttrValue(index, "Name")))
{
for (int i = 0; i < Program.PendingJobsArray.Count; ++i)
{
if (xmlParser.getElementAttrValue(index, "State") == "Write")
{
Program.PendingJobsArray.RemoveAt(index);
}
else
{
PendingJobs.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
PendingJobs.jobObjectState = xmlParser.getElementAttrValue(index, "State");
PendingJobs.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
PendingJobs.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
PendingJobs.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
PendingJobs.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
PendingJobs.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
PendingJobs.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
PendingJobs.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
Program.PendingJobsArray[index] = PendingJobs;
}
}
}
else if (Program.KnownPendingJobs.ContainsKey(xmlParser.getElementAttrValue(index, "Name")))
}
else
{
Program.KnownPendingJobs.Add(xmlParser.getElementAttrValue(index, "Name"), xmlParser.getElementAttrValue(index, "DateTime"));
JobObject PendingJobs = new JobObject();
PendingJobs.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
PendingJobs.jobObjectState = xmlParser.getElementAttrValue(index, "State");
PendingJobs.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
PendingJobs.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
PendingJobs.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
PendingJobs.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
PendingJobs.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
PendingJobs.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
PendingJobs.jobObjectQuantity = xmlParser.getElementAttrValue(index, "Quantity");
PendingJobs.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
//PendingJobs.jobObjectServer = xmlParser.getElementAttrValue("Server");
//PendingJobs.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
Program.PendingJobsArray.Add(PendingJobs);
}
}
Updated code based on comments below,
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
namespace SOERequestProgress
{
class XmlSplitter
{
// Declarations
private static string[] COMPLETED_STATES = new string[2]
{
"ERROR",
"COMPLETE"
};
private static string[] PENDING_STATES = new string[1]
{
"PENDING"
};
private static string[] ACTIVE_STATES = new string[1]
{
"PAUSED"
};
private const string STATE_ERROR = "ERROR";
private const string STATE_COMPLETE = "COMPLETE";
private const string STATE_PENDING = "PENDING";
private const string STATE_ACTIVE = "PAUSED";
public static JobObject PendingJobs = new JobObject();
public static JobObject CompletedJobs = new JobObject();
public static JobObject ActiveJobs = new JobObject();
//public static JobObject x;
public static int intI = 0;
// Add additional info from parsed XML (Currently only "Name" and "State" are collected.
public static void splitXml(string JobList, out List<JobObject> Active, out List<JobObject> Completed, out List<JobObject> Pending)
{
bool flag = false;
int num1 = 0;
string text = "";
string xmlString = "";
while (!flag)
{
if (num1 < 3)
{
try
{
xmlString = JobList;
flag = true;
}
catch (Exception ex)
{
++num1;
text = text + "Try (" + num1.ToString() + ") " + ex.Message + "\n";
Thread.Sleep(1000);
}
}
else
break;
}
if (!flag)
{
int num2 = (int)MessageBox.Show(text, "Job list error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
else
{
XmlParser xmlParser = new XmlParser();
if (!xmlParser.setXmlString(xmlString))
{
int num3 = (int)MessageBox.Show("Error parsing job list server response.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
flag = false;
}
else
{
int num3 = xmlParser.setElementName("Job");
for (int index = 0; index < num3; ++index)
{
string elementAttrValue1 = xmlParser.getElementAttrValue(index, "Name");
string elementAttrValue2 = xmlParser.getElementAttrValue(index, "State");
if (isState(elementAttrValue2, PENDING_STATES))
{
updatePending(index, xmlParser);
}
else if (isState(elementAttrValue2, COMPLETED_STATES))
{
updateCompleted(index, xmlParser);
}
else //if (isState(elementAttrValue2, ACTIVE_STATES))
{
updateActive(index, xmlParser);
}
}
}
}
Active = Program.ActiveJobsArray;
Completed = Program.CompletedJobsArray;
Pending = Program.PendingJobsArray;
}
private static void updatePending(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var pendingJob = Program.PendingJobsArray.Find(x => x.jobObjectName == jobName);
if (pendingJob == null)
{
JobObject newPendingJob = CreateJob("Pending", index, xmlParser);
Program.PendingJobsArray.Add(newPendingJob);
}
}
private static void updateCompleted(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var CompletedJob = Program.CompletedJobsArray.Find(x => x.jobObjectName == jobName);
if (CompletedJob == null)
{
JobObject newCompletedJob = CreateJob("Completed", index, xmlParser);
Program.CompletedJobsArray.Add(newCompletedJob);
}
}
private static void updateActive(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var activeJob = Program.ActiveJobsArray.Find(x => x.jobObjectName == jobName);
if (activeJob == null)
{
JobObject newActiveJob = CreateJob("Active", index, xmlParser);
Program.ActiveJobsArray.Add(newActiveJob);
}
}
private static JobObject CreateJob(string jobType, int index, XmlParser xmlParser)
{
if (jobType == "Pending")
{
JobObject x = new JobObject();
x.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
x.jobObjectState = xmlParser.getElementAttrValue(index, "State");
x.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
x.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
x.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
x.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
x.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
x.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
x.jobObjectQuantity = xmlParser.getElementAttrValue(index, "Quantity");
x.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
//x.jobObjectServer = xmlParser.getElementAttrValue("Server");
//x.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
return x;
}
if (jobType == "Completed")
{
JobObject x = new JobObject();
x.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
x.jobObjectState = xmlParser.getElementAttrValue(index, "State");
x.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
x.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
x.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
x.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
x.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
x.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
//x.jobObjectServer = xmlParser.getElementAttrValue("Server");
//x.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
return x;
}
if (jobType == "Active")
{
JobObject x = new JobObject();
x.jobObjectName = xmlParser.getElementAttrValue(index, "Name");
x.jobObjectState = xmlParser.getElementAttrValue(index, "State");
x.jobObjectDiscName = xmlParser.getElementAttrValue(index, "DiscName");
x.jobObjectPercentComplete = Convert.ToInt32(xmlParser.getElementAttrValue(index, "TotalPercentComplete"));
x.jobObjectDateTime = xmlParser.getElementAttrValue(index, "DateTime");
x.jobObjectRequested = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Requested"));
x.jobObjectCompleted = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Completed"));
x.jobObjectFailed = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Failed"));
x.jobObjectPriority = Convert.ToInt32(xmlParser.getElementAttrValue(index, "Priority"));
//x.jobObjectServer = xmlParser.getElementAttrValue("Server");
//x.jobObjectOperation = xmlParser.getElementAttrValue("Operation");
return x;
}
return null;
}
private static bool isState(string jobState, string[] knownStates)
{
bool flag = false;
for (int index = 0; index < knownStates.Length; ++index)
{
if (jobState.ToUpper().Equals(knownStates[index]))
{
flag = true;
//break;
}
}
return flag;
}
}
}
This new code doesn't appear to be correct and I cant see where its failing, but its filling my CompletedJobArray with over 200 entries in a matter of seconds.
My ListView is only showing 1 entry (its filtered for unique jobs) and on checking the contents of the Array, its adding the same jobObject for all 200+ entries.
Can anyone spot the flaw in this code?
Update 2: IEqualityComparer implemented, further errors encountered.
The below code are the implementation of Equals and GetHashCode.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SOERequestProgress
{
class JobComparer : IEqualityComparer<JobObject>
{
public JobComparer(Func<JobObject, object> keySelector)
{
KeySelector = keySelector;
}
public bool Equals(JobObject x, JobObject y)
{
return KeySelector(x).Equals(KeySelector(y));
//x.jobObjectName == y.jobObjectName &&
//x.jobObjectDateTime == y.jobObjectDateTime;
}
public int GetHashCode(JobObject job)
{
return KeySelector(job).GetHashCode();
}
}
}
This implementation throws the errors below,
Error 1 'SOERequestProgress.JobObject' does not implement interface member 'System.Collections.Generic.IEqualityComparer<SOERequestProgress.JobComparer>.GetHashCode(SOERequestProgress.JobComparer)' C:\Users\youngs\Documents\Visual Studio 2012\Projects\SOERequestProgress\SOERequestProgress\SOERequestProgress\JobObject.cs 9 11 SOERequestProgress
Error 2 'SOERequestProgress.JobObject' does not implement interface member 'System.Collections.Generic.IEqualityComparer<SOERequestProgress.JobComparer>.Equals(SOERequestProgress.JobComparer, SOERequestProgress.JobComparer)' C:\Users\youngs\Documents\Visual Studio 2012\Projects\SOERequestProgress\SOERequestProgress\SOERequestProgress\JobObject.cs 9 11 SOERequestProgress
Update 3: Ok I'm nearly about to admit defeat, as I don't seem to be able to get my head round how the IEqualityComparer works... and I'm back to banging me head on the keyboard >.<
The code below now gets 72 jobs in the CompletedJobsArray, which equates to 24 jobs duplicated 3 times!!!
private static void updateCompleted(int index, XmlParser xmlParser)
{
var jobName = xmlParser.getElementAttrValue(index, "Name");
var jobDate = xmlParser.getElementAttrValue(index, "DateTime");
// Commented line below has same affect as uncommented line, except it always returns true....
//var CompletedJobs = Program.CompletedJobsArray.Distinct(new JobComparer(x => new { x.jobObjectName, x.jobObjectDateTime }));
var CompletedJobs = Program.CompletedJobsArray.Find(x=>x.Equals(jobDate));
if (CompletedJobs == null)
{
JobObject newCompletedJob = CreateJob("Completed", index, xmlParser);
Program.CompletedJobsArray.Add(newCompletedJob);
}
else
{
// Code below causes issues with IEnumerable
//UpdateJob("Completed", index, CompletedJobs, xmlParser);
}
}
I have 24 jobs, but when the poll of the 3 locations for these job files happens, I only need the 24 unique jobs added to the array.... What am I doing wrong?
Update 4 Duplication still occurs... but not in the List...
Ok, so a little further on, and I am finding that my List conatins the correct number of jobs, with no duplication occuring.
However, when a new job enters the List, output from the program (in the UI) gets duplicated for some jobs but not all.
I cant seem to pin down a reason for this, as I've tried to keep things as simple as possible, and am using one list (which is confirmed as working as intended) and am generating my output to a listview from there.
Heres offending section of code,
private void SortViews()
{
var Jobs = Program.JobsArray;
Program.JobsArray.Sort((a, b) => String.Compare(a.jobObjectDateTime, b.jobObjectDateTime));
if (Jobs.Count >= 0)
{
foreach (JobObject Job in Jobs.Where(x => PENDING_STATES.Contains(x.jobObjectState)))
{
if (Job.inPendingView && Job.inPendingView)
{
Job.inCompletedView = false;
CompletedJobsView.Items.Remove(PendingJobsView.FindItemWithText(Job.jobObjectName));
Job.inActiveView = false;
ActiveJobsView.Items.Remove(ActiveJobsView.FindItemWithText(Job.jobObjectName));
ListViewItem item = PendingJobsView.FindItemWithText(Job.jobObjectDateTime);
item.SubItems[0].Text = Job.jobObjectName;
item.SubItems[1].Text = Convert.ToString(Job.jobObjectPriority);
item.SubItems[2].Text = Job.jobObjectDiscName;
item.SubItems[3].Text = Convert.ToString(Job.jobObjectRequested);
item.SubItems[4].Text = Job.jobObjectDateTime;
}
else
{
string[] brr = new string[6];
ListViewItem item;
brr[0] = Job.jobObjectName;
brr[1] = Convert.ToString(Job.jobObjectPriority);
brr[2] = Job.jobObjectDiscName;
brr[3] = Convert.ToString(Job.jobObjectRequested);
brr[4] = Job.jobObjectDateTime;
item = new ListViewItem(brr);
item.ForeColor = Color.Blue;
PendingJobsView.Items.Add(item);
Job.inPendingView = true;
Job.inActiveView = false;
Job.inCompletedView = false;
Job.isActive = false;
Job.isCompleted = false;
Job.isPending = true;
}
}
foreach (JobObject Job in Jobs.Where(x => ACTIVE_STATES.Contains(x.jobObjectState)))
{
if (Job.isActive && Job.inActiveView)
{
Job.inPendingView = false;
PendingJobsView.Items.Remove(PendingJobsView.FindItemWithText(Job.jobObjectName));
Job.inCompletedView = false;
CompletedJobsView.Items.Remove(CompletedJobsView.FindItemWithText(Job.jobObjectDateTime));
//ActiveJobsView.Refresh();
ListViewItem item = ActiveJobsView.FindItemWithText(Job.jobObjectName);
item.SubItems[0].Text = Job.jobObjectName;
item.SubItems[1].Text = Convert.ToString(Job.jobObjectPriority);
item.SubItems[2].Text = Job.jobObjectDiscName;
item.SubItems[3].Text = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
item.SubItems[4].Text = Convert.ToString(Job.jobObjectFailed);
item.SubItems[5].Text = Convert.ToString(Job.percentComplete) + "%";
item.SubItems[6].Text = Convert.ToString(Job.jobObjectState);
}
else
{
Job.isActive = true;
string[] crr = new string[7];
ListViewItem item;
crr[0] = Job.jobObjectName;
crr[1] = Convert.ToString(Job.jobObjectPriority);
crr[2] = Job.jobObjectDiscName;
crr[3] = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
crr[4] = Convert.ToString(Job.jobObjectFailed);
crr[5] = Convert.ToString(Job.percentComplete) + "%";
crr[6] = Convert.ToString(Job.jobObjectState);
item = new ListViewItem(crr);
item.ForeColor = Color.DarkOrange;
ActiveJobsView.Items.Add(item);
Job.inActiveView = true;
Job.inPendingView = false;
Job.inCompletedView = false;
Job.isActive = true;
Job.isCompleted = false;
Job.isPending = false;
}
}
foreach (JobObject Job in Jobs.Where(x => COMPLETED_STATES.Contains(x.jobObjectState)))
{
if (Job.isCompleted && Job.inCompletedView)
{
Job.inPendingView = false;
PendingJobsView.Items.Remove(PendingJobsView.FindItemWithText(Job.jobObjectName));
Job.inActiveView = false;
ActiveJobsView.Items.Remove(ActiveJobsView.FindItemWithText(Job.jobObjectName));
//CompletedJobsView.Refresh();
ListViewItem item = CompletedJobsView.FindItemWithText(Job.jobObjectName);
item.SubItems[0].Text = Job.jobObjectName;
item.SubItems[1].Text = Job.jobObjectDiscName;
item.SubItems[2].Text = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
item.SubItems[3].Text = Convert.ToString(Job.jobObjectFailed);
item.SubItems[4].Text = Convert.ToString(Job.jobObjectState);
item.SubItems[5].Text = Job.jobObjectDateTime;
}
else
{
string[] arr = new string[6];
ListViewItem item;
arr[0] = Job.jobObjectName;
arr[1] = Job.jobObjectDiscName;
arr[2] = Convert.ToString(Job.jobObjectCompleted) + " of " + Convert.ToString(Job.jobObjectRequested);
arr[3] = Convert.ToString(Job.jobObjectFailed);
arr[4] = Convert.ToString(Job.jobObjectState);
arr[5] = Job.jobObjectDateTime;
item = new ListViewItem(arr);
if (Job.jobObjectState == "ERROR")
{
item.ForeColor = Color.Firebrick;
}
if (Job.jobObjectState == "COMPLETE")
{
item.ForeColor = Color.Green;
}
CompletedJobsView.Items.Add(item);
Job.inCompletedView = true;
Job.inPendingView = false;
Job.inActiveView = false;
Job.isCompleted = true;
Job.isActive = false;
Job.isPending = false;
}
}
}
}
Any ideas whats causing this behavior?
Thanks again :)
Upvotes: 1
Views: 2371
Reputation: 2818
Instead of complicating the code, it would be easy to work with a single list here.
List<JobObject> jobs = new List<JobObject>();
// Code that adds / updates a job
var newJob = CreateJob(state);
// The equality comparer should check for a job name and job date of two jobs to ensure the uniqueness
var existingJob = jobs.Find(x => x.Equals(x, newJob));
if(existingJob != null)
{
// Update your existing job
// If you want to calulate the time interval between job start till end, you can have more properties
// indicating the jobPendingStartTime, jobActiveStartTime etc.
}
else
{
jobs.Add(newJob);
}
// Code to find jobs
var pendingJobs = jobs.Where(x => PENDING_STATES.contains(x.jobObjectState));
var activeJobs = jobs.Where(x => ACTIVE_STATES.contains(x.jobObjectState));
var completedJobs = jobs.Where(x => COMPLETED_STATES.contains(x.jobObjectState));
// Bind to different list views
IEqualityComparer Implementation:
public class JobObject
{
public string Name { get; set; }
public string Date { get; set; }
//Other properties
}
public class JobObjectComparer : IEqualityComparer<JobObject>
{
public bool Equals(JobObject x, JobObject y)
{
return x.Name == y.Name &&
x.Date == y.Date;
}
public int GetHashCode(JobObject job)
{
return job.GetHashCode();
}
// Then use it as follows.
// JobObject newJob = CreateJob();
// var comparer = new JobObjectComparer();
// var existingJob = jobsArray.Find(x => comparer.Equals(x, newJob));
// if(existingJob != null)
// {
// Job Exists
// }
}
Let me know if this helps.
Upvotes: 0
Reputation: 23561
Define a custom IEqualityComparer for your PendingJobs object. Then you can easily tweak the comparison as you see fit and compare all the fields or a subset of fields. Then you can create a HashSet and feed it this comparer. You can also use a dictionary if it is better as it also accepts IEqualityComparer.
Upvotes: 2