What is my foreach mistake? - c#

I have a problem with my foreach loop. The prupose is to loop through items in a listbox and for every item there is, it should set the properties of the person equal to the properties of the person equal to a person object which i will insert into a list of persons.(The person has a item, with properties ect..). Problem: It inserts the first person with it's item into the list, but when it comes to the second person that it has to insert it changes the first person data to the same data as the second person data, and inserts the second person. So it always inserts the new person but changes all my old data that I have alredy inserted too be the same as the new person's.
private void btnOK_Click(object sender, EventArgs e)
{
bool bOK = false;
if (UC.IsEmpty(txtFirstName) || UC.IsEmpty(txtLastName) || UC.IsEmpty(txtID) || lstItemsAdded.Text == null) //Maak seker van die listItemsAdded se content... hier sal n error wees... j kan nog n else maak dat hy spesefiek toets of daar items in die lstbox is
{
UC.MB("Customer Information Missing", "Please supply enough customer information");
}
else
{
bOK = true;
}
if (bOK)
{
foreach (Item item in lstItemsAdded.Items)
{
PersonItemObject.FirstName = txtFirstName.Text;
PersonItemObject.LastName = txtLastName.Text;
PersonItemObject.ID = txtID.Text;
PersonItemObject.Email = txtEmail.Text;
PersonItemObject.Age = Convert.ToInt32(txtAge.Text);
PersonItemObject.Item.ItemCode = item.ItemCode;
PersonItemObject.Item.ItemDescription = item.ItemDescription;
PersonItemObject.Item.ItemName = item.ItemName;
PersonItemObject.Item.ItemPrice = item.ItemPrice;
It is supposed to add all the items in the listBox to the list in the next statement, and for each item it should add the persons details too.
PersonItemsList.Add(PersonItemObject);
If I added more than 1 item to a person it changes my old data that i have added in the list to be the same as then new person data, and inserts a new person into the list too.
}
DialogResult = DialogResult.OK;
Close();
}
}

In every iteration you are updating properties of the same object, and then inserting it into the list. SO in the end list contains several referencies to the same object.
What you should do is craeting new object for each iteration:
foreach (Item item in lstItemsAdded.Items)
{
PersonItem item = new PersonItem(); //just guessing the type here
item.FirstName = txtFirstName.Text;
...
PersonItemsList.Add(item);
}

You should create a new instance of your "PersonItemObject". Something like:
PersonItemObject = new PersonItemObjectClass()
as the first sentence of your loop, being PersonItemObjectClass the type of PersonItemObject. The problem here is, probably, you are using always the same instance and because of it the value is always changing.

You've got to create a new instance of whatever type PersonItemObject is, inside the foreach loop.
What you're adding to the PersonItemsList is actually a reference to a single instance of your class. Every time you iterate the loop, you're updating the same instance, so you have a list of identical looking objects.
foreach (Item item in lstItemsAdded.Items)
{
var PersonItemObject = new PersonItem();
PersonItemObject.FirstName = txtFirstName.Text;
PersonItemObject.LastName = txtLastName.Text;
...
PersonItemsList.Add(PersonItemObject);
}
You may want to read up on the differences between value types and reference types.

You have one PersonItemObject and you are changing it in every iteration.If you want to make a list of PersonItemObjects then create a new instance on each iteration:
foreach (Item item in lstItemsAdded.Items)
{
var PersonItemObject = new YourType();
PersonItemObject.FirstName = txtFirstName.Text;
PersonItemObject.LastName = txtLastName.Text;
...
}

This is happening because you're always changing the same PersonItemObject. You should create a new version of this object each time and add it your list.
foreach (Item item in lstItemsAdded.Items)
{
var newObject = new PersonItemObject();
newObject.FirstName = txtFirstName.Text;
newObject.LastName = txtLastName.Text;
newObject.ID = txtID.Text;
newObject.Email = txtEmail.Text;
newObject.Age = Convert.ToInt32(txtAge.Text);
newObject.Item.ItemCode = item.ItemCode;
newObject.Item.ItemDescription = item.ItemDescription;
newObject.Item.ItemName = item.ItemName;
newObject.Item.ItemPrice = item.ItemPrice;
PersonItemsList.Add(newObject);
}

You are not creating a new object each time you are setting the values to it. To create a new object the proper way would be like this:
foreach (Item item in lstItemsAdded.Items)
{
var newObject = new PersonItemObject()
{
FirstName = txtFirstName.Text;
LastName = txtLastName.Text;
ID = txtID.Text;
Email = txtEmail.Text;
Age = Convert.ToInt32(txtAge.Text);
Item.ItemCode = item.ItemCode;
Item.ItemDescription = item.ItemDescription;
Item.ItemName = item.ItemName;
Item.ItemPrice = item.ItemPrice;
}
PersonItemsList.Add(newObject);
}

Related

Why is my gridview populating the incorrect data in my rows?

I am building a bookstore using GridViews and data from my database. There are checkboxes and each row has a quantity textbox. I am validating to make sure the at least one checkbox is checked and that the selected row has a quantity input before hitting submit. When the user hits submit, the data selected should be populated into another gridview.
The issue i am having is that when i select two different books and hit submit, the books populated on the gridview is just repeating only one book twice.
*Also the lblError text is still showing when i set the visibility to false when I submit.
Here's a snippet of the submit button call:
protected void btnSubmit_Click(object sender, EventArgs e)
{
double saleCount = 0;
Processor p = new Processor();
Book objBook = new Book();
foreach (GridViewRow row in gvBooks.Rows)
{
CheckBox chkbx = (CheckBox)row.Cells[0].FindControl("cbBook");
string title = row.Cells[1].Text;
string authors = row.Cells[2].Text;
string isbn = row.Cells[3].Text;
DropDownList gvType = (DropDownList)row.Cells[4].FindControl("ddBookType");
DropDownList gvMethod = (DropDownList)row.Cells[5].FindControl("ddMethod");
TextBox qty = (TextBox)row.Cells[6].FindControl("txtQty");
String strType = Convert.ToString(gvType.Text);
String strMethod = Convert.ToString(gvMethod.Text);
if (chkbx.Checked && !(string.IsNullOrEmpty(qty.Text)))
{
panelHeader.Visible = false;
panelStudentInfo.Visible = false;
panelCampus.Visible = false;
panelCatalog.Visible = false;
panelStudentInfo2.Visible = true;
panelCampus2.Visible = true;
panelCatalog2.Visible = true;
gvBooks.Visible = false;
gvOrder.Visible = true;
panelButtons.Visible = false;
txtStudentID2.Text = txtStudentID.Text;
txtStudentName2.Text = txtStudentName.Text;
txtStudentAddr2.Text = txtStudentAddr.Text;
txtPhoneNo2.Text = txtPhoneNo.Text;
ddCampus2.Text = ddCampuses.Text;
lblError.Visible = false;
int quantity = Convert.ToInt32(qty.Text);
objBook.Title = title;
objBook.Authors = authors;
objBook.ISBN = isbn;
objBook.BookType = strType;
objBook.Method = strMethod;
objBook.Quantity = quantity;
objBook.Price = p.Calculate(isbn, strType, strMethod);
objBook.TotalCost = objBook.Price * objBook.Quantity;
orderList.Add(objBook);
saleCount += objBook.Quantity;
orderTotal = objBook.TotalCost + orderTotal;
p.UpdateDB(isbn, quantity, strMethod, objBook.TotalCost);
}
else
{
lblError.Text = "* Please select a book & enter a quantity";
lblError.Visible = true;
}
gvOrder.DataSource = orderList;
gvOrder.DataBind();
gvOrder.Columns[0].FooterText = "Totals";
gvOrder.Columns[5].FooterText = saleCount.ToString();
gvOrder.Columns[6].FooterText = orderTotal.ToString("C2");
}
}
You need to change your code from this
Book objBook = new Book();
foreach (GridViewRow row in gvBooks.Rows)
{
....
to this
foreach (GridViewRow row in gvBooks.Rows)
{
Book objBook = new Book();
.....
The reason is simple. If you create the Book instance outside the loop and, inside the loop, you set its properties and add it to the list, at the second loop you will change the properties of the same instance to different values and add the reference a second time to the list. At the end of the loop your list will have many references to the SAME instance and this single instance will have its properties set to the last values read inside the loop.
If you declare and initialize the Book instance inside the loop you have, at every loop, a different instance of Book to add to the list and each instance will have its own property values.
Looking better at your code, I think that all the code after the if check should go outside the loop even the setting of the datasource.
Here a stripped down layout of the code to highlight the relevant points.
protected void btnSubmit_Click(object sender, EventArgs e)
{
double saleCount = 0;
Processor p = new Processor();
// Prepare a list of errors
List<string> errors = new List<strig>();
foreach (GridViewRow row in gvBooks.Rows)
{
Book objBook = new Book();
....
if (chkbx.Checked)
{
// Probably it is better to check here also the quantity value
// not just for text in the textbox (it could be anything)
if(Int32.TryParse(qty.Text, out int quantity) && quantity > 0)
{
// We have at least one checkbox checked with a quantity, so no error!
.....
}
else
{
// We don't have a quantity, add book title to error list....
errors.Add($"Book {title} has no quantity!");
}
}
}
// Handle the errors, if any
if(errors.Count > 0)
{
lblError.Text = string.Join("<br/>, errors);
lblError.Visible = true;
}
else
{
lblError.Visible = false;
gvOrder.DataSource = orderList;
gvOrder.DataBind();
gvOrder.Columns[0].FooterText = "Totals";
gvOrder.Columns[5].FooterText = saleCount.ToString();
gvOrder.Columns[6].FooterText = orderTotal.ToString("C2");
}
}

how to separate the "collection" from creating and adding a new instance of "lookaheadRunInfo"

I am trying to "collect" the GetString(2) until GetString(0) changes,so am trying to find out how to separate the "collection" from creating and adding a new instance of "lookaheadRunInfo"?I have tried as below which throws an exception
System.NullReferenceException was unhandled by user code at line
lookaheadRunInfo.gerrits.Add(rdr.GetString(1)); ,can anyone provide guidance on how to fix this issue?
try
{
Console.WriteLine("Connecting to MySQL...");
conn.Open();
string sql = #"select lr.ec_job_link, cl.change_list ,lr.submitted_by, lr.submission_time,lr.lookahead_run_status
from lookahead_run as lr, lookahead_run_change_list as lrcl, change_list_details as cld,change_lists as cl
where lr.lookahead_run_status is null
and lr.submission_time is not null
and lrcl.lookahead_run_id = lr.lookahead_run_id
and cl.change_list_id = lrcl.change_list_id
and cl.change_list_id not in (select clcl.change_list_id from component_labels_change_lists as clcl)
and cld.change_list_id = lrcl.change_list_id
group by lr.lookahead_run_id, cl.change_list
order by lr.submission_time desc
limit 1000
";
MySqlCommand cmd = new MySqlCommand(sql, conn);
MySqlDataReader rdr = cmd.ExecuteReader();
var ECJoblink_previous ="";
var gerritList = new List<String>();
while (rdr.Read())
{
//Console.WriteLine(rdr[0] + " -- " + rdr[1]);
//Console.ReadLine();
var lookaheadRunInfo = new LookaheadRunInfo();
lookaheadRunInfo.ECJobLink = rdr.GetString(0);
if (ECJoblink_previous == lookaheadRunInfo.ECJobLink)
{
//Keep appending the list of gerrits until we get a new lookaheadRunInfo.ECJobLink
lookaheadRunInfo.gerrits.Add(rdr.GetString(1));
}
else
{
lookaheadRunInfo.gerrits = new List<string> { rdr.GetString(1) };
}
ECJoblink_previous = lookaheadRunInfo.ECJobLink;
lookaheadRunInfo.UserSubmitted = rdr.GetString(2);
lookaheadRunInfo.SubmittedTime = rdr.GetString(3).ToString();
lookaheadRunInfo.RunStatus = "null";
lookaheadRunInfo.ElapsedTime = (DateTime.UtcNow-rdr.GetDateTime(3)).ToString();
lookaheadRunsInfo.Add(lookaheadRunInfo);
}
rdr.Close();
}
catch
{
throw;
}
If I understand your requirements correctly, you wish to keep a single lookaheadRunInfo for several rows of the resultset, until GetString(0) changes. Is that right?
In that case you have some significant logic problems. The way it is written, even if we fix the null reference, you will get a new lookaheadRunInfo with each and every row.
Try this:
string ECJoblink_previous = null;
LookAheadRunInfo lookaheadRunInfo = null;
while (rdr.Read())
{
if (ECJoblink_previous != rdr.GetString(0)) //A new set of rows is starting
{
if (lookaheadRunInfo != null)
{
lookaheadRunsInfo.Add(lookaheadRunInfo); //Save the old group, if it exists
}
lookaheadRunInfo = new LookAheadRunInfo //Start a new group and initialize it
{
ECJobLink = rdr.GetString(0),
gerrits = new List<string>(),
UserSubmitted = rdr.GetString(2),
SubmittedTime = rdr.GetString(3).ToString(),
RunStatus = "null",
ElapsedTime = (DateTime.UtcNow-rdr.GetDateTime(3)).ToString()
}
}
lookahead.gerrits.Add(rdr.GetString(1)); //Add current row
ECJoblink_previous = rdr.GetString(0); //Keep track of column 0 for next iteration
}
if (lookaheadRunInfo != null)
{
lookaheadRunsInfo.Add(lookaheadRunInfo); //Save the last group, if there is one
}
The idea here is:
Start with a blank slate, nothing initialized
Monitor column 0. When it changes (as it will on the first row), save any old list and start a new one
Add to current list with each and every iteration
When done, save any remaining items in its own list. A null check is required in case the reader returned 0 rows.

Each row iterate through n columns

I have the code below. To explain there will always be values for the 'tl' variable.
At the moment its hard coded to always assume 4 columns in the row, but I want to make it work based on the count of the columns and make it build the levels based on how many columns there are, but there also needs to be a value in the column.
So at the moment if there is a value in column 2, it will build the 'ltwo' variable, and then if there is a value in column 3 it does the 'lthree'.
I want to make it build as many levels as it needs to so im not repeating code and having the same code over and over.
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
foreach (DataRow row in dataTable.Rows)
{
var tl = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
if (!String.IsNullOrEmpty(row[2].ToString()))
{
var ltwo = new AdditionalPropertyType
{
Name = row[2].ToString()
};
var ltwolist = new List<AdditionalPropertyType>();
ltwolist.Add(tl);
ltwo.AdditionalProperties = ltwolist;
if (!String.IsNullOrEmpty(row[3].ToString()))
{
var lthree = new AdditionalPropertyType
{
Name = row[3].ToString()
};
var lthreelist = new List<AdditionalPropertyType>();
lthreelist.Add(ltwo);
lthree.AdditionalProperties = lthreelist;
currentadditionalproperties.Insert(0, lthree);
}
else
currentadditionalproperties.Insert(0, ltwo);
}
else
currentadditionalproperties.Insert(0, tl);
}
return currentadditionalproperties;
}
You can get the columns using the Columns property of the DataTable:
foreach (DataRow row in dataTable.Rows)
{
foreach(DataColumn column in dataTable.Columns)
{
Trace.WriteLine(column.ColumnName + " = " + row[column]);
}
}
You probably want to do something like this: (written on the websites, some minor typos can be present)
You need to iterate the additional columns and check if there is a value present. When there is a value, create a backup reference and renew your property.
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
// check if there are atleast 2 columns defined
if(dataTable.Columns.Count < 2)
throw new Exception("At least two columns are required");
// The result
var currentadditionalproperties = new List<AdditionalPropertyType>();
// iterate the rows
foreach (DataRow row in dataTable.Rows)
{
// create the base property
var tl = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
// check the rest of the columns for additional names
foreach(int index=2;index<dataTable.Columns.Count;index++)
{
var columnValue = row[index].ToString();
// if the column is empty, discontinue the iteration
if(String.IsNullOrEmpty(columnValue))
break;
// create a backup reference.
var previous = tl;
// create a new AdditionalPropertyType
var tl = new AdditionalPropertyType { Name = columnValue };
// Create the list
tl.AdditionalProperties = new List<AdditionalPropertyType>();
// add the previous (backup reference)
tl.AdditionalProperties.Add(previous);
}
// insert the 'chain' of additional properties on the list at possition 0
currentadditionalproperties.Insert(0, tl);
}
// return the list
return currentadditionalproperties;
}
The first step is to reverse your condition and make use of the keyword continue
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
foreach (DataRow row in dataTable.Rows)
{
var tl = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
if (String.IsNullOrEmpty(row[2].ToString())){
currentadditionalproperties.Insert(0, tl);
continue;
}
var ltwo = new AdditionalPropertyType
{
Name = row[2].ToString()
};
var ltwolist = new List<AdditionalPropertyType>();
ltwolist.Add(tl);
ltwo.AdditionalProperties = ltwolist;
if (String.IsNullOrEmpty(row[3].ToString())) {
currentadditionalproperties.Insert(0, ltwo);
continue;
}
var lthree = new AdditionalPropertyType
{
Name = row[3].ToString()
};
var lthreelist = new List<AdditionalPropertyType>();
lthreelist.Add(ltwo);
lthree.AdditionalProperties = lthreelist;
currentadditionalproperties.Insert(0, lthree);
}
return currentadditionalproperties;
}
Now, the code is clearer. The next step is to collect the repeating cases. Note the second case onward is repeating. Thus, do further simplification:
public static List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable, List<AdditionalPropertyType> currentadditionalproperties)
{
foreach (DataRow row in dataTable.Rows)
{
var tlprev = new AdditionalPropertyType
{
Name = row[0].ToString(),
Value = row[1].ToString()
};
bool isTlUpdated = true;
for (int i = 2; i <= 3; ++i) { //change this according to your need
if (String.IsNullOrEmpty(row[i].ToString()) && isTlUpdated){
currentadditionalproperties.Insert(0, tlprev);
isTlUpdated = false;
break; //note that this will now change to break to break from the current for-loop
}
var lnext = new AdditionalPropertyType
{
Name = row[i].ToString()
};
var lnextlist = new List<AdditionalPropertyType>();
lnextlist.Add(tlprev);
lnext.AdditionalProperties = lnextlist;
tlprev = lnext; //need to record this for the next loop or end of the case
isTlUpdated = true;
}
if (isTlUpdated) //correction by Jeroen
currentadditionalproperties.Insert(0, tlprev);
}
return currentadditionalproperties;
}
The key is to simplify the code step-by-step
You haven't posted all your code, so I had to guess in a couple of places (such as what the "currentAdditionalProperties" does).
I think that the below code illustrates what you want to do by making the logic extendable depending on how many columns the data table has.
The trick is to just store the "last thing" in a variable, so it can be used for the "current thing". At the end, whatever was the "last thing" is what you want to store in your "currentAdditionalProperties" object. I have commented so you can see the logic.
private List<AdditionalPropertyType> SQLAddPropsStructured(DataTable dataTable)
{
AdditionalPropertyType lastNewType; // to remember the previous new instance
// for all rows...
foreach (DataRow row in dataTable.Rows)
{
// the first type takes name and value from the first two fields
AdditionalPropertyType newType = new AdditionalPropertyType();
newType.Name = row[0].ToString();
newType.Value = row[1].ToString();
// remember this type: it is used as the AdditionalProperties for the NEXT type
lastNewType = newType;
// additional types start from field 2
int field = 2;
// iterate until we find a NULL field.
// If you want to check for the end of the fields rather than a NULL value, then instead use:
// while(field < dataTable.Columns.Count)
while(!String.IsNullOrEmpty(row[field].ToString()))
{
// create new type
var newSubType = new AdditionalPropertyType();
// get name
Name = row[field].ToString();
// new type takes the PREVIOUS type as its additional parameters
List<AdditionalPropertyType> propertyData = new List<AdditionalPropertyType>();
propertyData.Add(lastNewType);
newSubType.AdditionalProperties = propertyData;
// remember THIS type for the NEXT type
lastNewType = newSubType;
// process next field (if valid)
field++;
}
// put the last set of properties found into the current properties
currentAdditionalProperties.Insert(0, lastNewType);
return currentAdditionalProperties;
}
}

ArgumentOutOfRangeException when writing to a model

I'm trying to write to a model for the first time to use in my view: the first time I write to the model I get an ArgumentOutOfRangeException.
Getting error on first write to array:
private IAdditionalQuestionsService _service;
private SelectedAdditionalQuestionAnswerModel _model;
private void InitializeController()
{
_service = GetObject<IAdditionalQuestionsService>();
//GetPageHeaderText(inst);
ViewBag.GetPageTitle = "Additional Questions";
}
[HttpGet]
public virtual ActionResult Edit()
{
Institution inst = _service.GetInstitution(State.GetInstitutionRecordId());
_model = GetObject<SelectedAdditionalQuestionAnswerModel>();
_model.AddQuestAnswModel = new List<AdditionalQuestionAnswerModel>();
GetPageConfiguration1(inst);
return View(_model);
}
AdditionalQuestionAnswerModel m = GetObject<AdditionalQuestionAnswerModel>();
int c = 0;
foreach (var x in inst.AdditionalQuestions)
{
foreach (var y in x.AdditionalQuestionAnswers)
{
// Error is happening on next line *************
_model.AddQuestAnswModel[c].QuestionText = x.QuestionText;
_model.AddQuestAnswModel[c].InstitutionId = x.InstitutionId;
_model.AddQuestAnswModel[c].AdditionalQuestionId = x.Id;
_model.AddQuestAnswModel[c].AnswerText = y.AnswerText;
_model.AddQuestAnswModel[c].IsSelected = false;
c++;
}
}
You can't use _model.AddQuestAnswModel[c] because you never added any items to your list.
Instead of that, create a new object and set its values and then add the item to your list.
Something like this:
AdditionalQuestionAnswerModel newItem = new AdditionalQuestionAnswerModel();
//set the values here to newItem
_model.AddQuestAnswModel.Add(newItem);
You're firstly instantiating your list
_model.AddQuestAnswModel = new List<AdditionalQuestionAnswerModel>();
then you try to access to the first element
_model.AddQuestAnswModel[c] // c == 0
without adding any element to the list.
Add an element before trying to access to a list by index, or more simple:
foreach (var y in x.AdditionalQuestionAnswers)
{
AdditionalQuestionAnswerModel newObj = new AdditionalQuestionAnswerModel
{
QuestionText = x.QuestionText;
InstitutionId = x.InstitutionId;
AdditionalQuestionId = x.Id;
AnswerText = y.AnswerText;
IsSelected = false;
};
_model.AddQuestAnswModel.Add(newObj);
}
Ir means that there no item in your _model.AddQuestAnswModel at the indicated postition, and from your code, I see that _model.AddQuestAnswModel has only be initiated with new List<AdditionalQuestionAnswerModel>(), so it does not contain items (unless you're doing it in the contructor).
You need to fill it like so :
_model.AddQuestAnswModel.Add(item);

Adding properties of an Object together in a collection

I have an app that creates ContactList Objects and adds them to a Dictionary collection. My ContactList objects have a property called AggLabels which is a collection of AggregatedLabel objects containg Name and Count properties. What I am trying to do is change the "else" case of my code snippet so that before adding a new AggregatedLabel it will check whether the AggLabel.Name exists in the AggregatedLabel collection and if this is true it will not add the AggLabel.Name again. Instead it will add the value of AggLabel.Count (type int) to the existing AggregatedLabel object. So for an existing object, if the first Count value was 3 and the second value is 2 then the new Count value should be 5. In simple terms I want to have unique AggLabel Names and add together the Counts where the Names are the same. Hope that makes sense - would appreciate any help. Thanks!
Code snippet
Dictionary<int, ContactList> myContactDictionary = new Dictionary<int, ContactList>();
using (DB2DataReader dr = command.ExecuteReader())
{
while (dr.Read())
{
int id = Convert.ToInt32(dr["CONTACT_LIST_ID"]);
if (!myContactDictionary.ContainsKey(id))
{
ContactList contactList = new ContactList();
contactList.ContactListID = id;
contactList.ContactListName = dr["CONTACT_LIST_NAME"].ToString();
//contactList.AggLabels = new ObservableCollection<AggregatedLabel>() { new AggregatedLabel() { Name = dr["LABEL_NAME"].ToString(), Count = Convert.ToInt32(dr["LABEL_COUNT"])}};
contactList.AggLabels = new ObservableCollection<AggregatedLabel>()
{
new AggregatedLabel()
{
Name = dr["LABEL_NAME"].ToString(),
Count = Convert.ToInt32(dr["LABEL_COUNT"])
}
};
myContactDictionary.Add(id, contactList);
}
else
{
ContactList contactList = myContactDictionary[id];
contactList.AggLabels.Add(
new AggregatedLabel()
{
Name = dr["LABEL_NAME"].ToString(),
Count = Convert.ToInt32(dr["LABEL_COUNT"])
}
);
}
}
}
There are two possible solutions I can think of:
1) Use a dictionary instead of the collection of aggregated labels the same way you do it for the contact dictionary. When yout use the name as key and the count as value, you can use the ContainsKey-Method to check whether the label already exists.
contactList.AggLabels = new Dictionary<string, int>();
...
else
{
ContactList contactList = myContactDictionary[id];
if (contactList.AggLabels.ContainsKey(dr["LABEL_NAME"].ToString()))
{
contactList.AggLabels[dr["LABEL_NAME"].ToString()] += Convert.ToInt32(dr["LABEL_COUNT"]);
}
else
{
contactList.AggLabels.Add(dr["LABEL_NAME"].ToString(), Convert.ToInt32(dr["LABEL_COUNT"]));
}
}
2) I you need to use the AggreagteLabel object you can use a loop to search throug all labels.
else
{
bool flagAggLabelFound = false;
ContactList contactList = myContactDictionary[id];
foreach(AggregateLabel aggLabel in contactList.AggLabels)
{
if(aggLabel.Name == dr["LABEL_NAME"].ToString())
{
aggLabel.Count += Convert.ToInt32(dr["LABEL_COUNT"]);
flagAggLabelFound = true;
break;
}
}
if (!flagAggLabelFound)
{
contactList.AggLabels.Add(
new AggregatedLabel()
{
Name = dr["LABEL_NAME"].ToString(),
Count = Convert.ToInt32(dr["LABEL_COUNT"])
}
);
}
}
I hope this helps.
I would try this:
ContactList contactList = myContactDictionary[id];
AggregateLabel existing = contactList.AggLabels.FirstOrDefault(
l => l.Name == dr["LABEL_NAME"].ToString()
);
if (existing == null) { contactList.AggLabels.Add(
new AggregatedLabel() {
Name = dr["LABEL_NAME"].ToString(),
Count = Convert.ToInt32(dr["LABEL_COUNT"])
}
);
}
else { existing.Count += Convert.ToInt32(dr["LABEL_COUNT"]); }
#extract these Aggregated Labels and put them in a separate Observable collection:
1) If you a Dictionary for storing the labels in the contact list, this should work:
ObservableCollection<AggregateLabel> copyOfAggregateLabels = new ObservableCollection<AggregateLabel>();
foreach (KeyValuePair<string, int> aggLabel in aggregateLabels)
{
copyOfAggregateLabels.Add(
new AggregatedLabel() {
Name = aggLabel.Key,
Count = aggLabel.Value
}
);
}
2) If you use an ObservableCollection of AggregateLabels, you get an AggregateLable instead of a KeyValuePair in the loop. The rest works the same way.
First I thought of something like:
ObservableCollection<AggregateLabel> copyOfAggregateLabels = new ObservableCollection<AggregateLabel>(aggregateLables);
But this way you get a new ObservableCollection, but the labels stored in the new collection are still referring to the same objects as the ones in the collection you copy.

Categories

Resources