Test data to build tree menu using recursive method? - c#

I can't seem to get my head around this. I need to build some test data to build a tree menu, but I either get a stackverflow error because it just keeps going or I only get the first level of children. Here is the code I currently have:
private MenuItem _menuItem;
private int _start = 1;
private const int Stop = 5;
public void BuildMenu()
{
var numberOfChildren = new Random((int) DateTime.Now.Ticks).Next(3, 40);
AutoMapper.Mapper.CreateMap<MenuItem, MenuItemDto>().ConvertUsing<MenuItemConverter>();
_menuItem = new MenuItem() {Id = Guid.NewGuid(), Name = "Main Menu"};
_menuItem.ChildMenuItems = BuildChildItems(_menuItem, numberOfChildren);
var menuDto = AutoMapper.Mapper.Map<MenuItem, MenuItemDto>(_menuItem);
}
public ICollection<MenuItem> BuildChildItems(MenuItem parentMenuItem, int numberOfChildren)
{
var childItems = new Collection<MenuItem>();
_start += 1;
for (var i = 0; i <= numberOfChildren; i++)
{
var childItem = new MenuItem()
{
Id = Guid.NewGuid(),
ParentItemId = parentMenuItem.Id,
Parentitem = parentMenuItem,
Name = "Child Menu Item " + DateTime.Now
};
if (_start != Stop)
{
childItem.ChildMenuItems = BuildChildItems(childItem, numberOfChildren);
}
childItems.Add(childItem);
}
return childItems;
}
public class MenuItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public Guid? ParentItemId { get; set; }
public MenuItem Parentitem { get; set; }
public ICollection<MenuItem> ChildMenuItems { get; set; }
}
While this works, you will notice the _start and Stop variables. I put those in so I don;t get a stackoverflow, but obviously that is why I only get the 1st level of each with populated children. I figure I would need to track what level I am in, but not sure how to keep track of it. Not sure why I am drawing a blank today....

This gives the idea rather than actually looking at your specific object model! Have an int which represents the number of levels you want to go to. Send this into the method, decrement it on each recurse until it is zero.
public void BuildChildItems(MenuItem parentMenuItem, int numberOfChildren, int level)
{
if(level == 0) return;
var results_for_this_level = create_results_for_this_level();
parentMenuItem.Add(results_for_this_level);
foreach(sibling in results_for_this_level)
{
BuildChildItems(sibling, numberofChildren, level-1) //note the decrement of level here
}
}

Related

Can't search for the max value into an array

My problem is that i can't search into an array using linq in order to find an object property and set that as an id.
I need the method to search in the array for other model.idCliente and set that value as the "nextid + 1", in order to use it as id and the next array index.
Since the array's empty, the program adds the new object correctly, but when entering in the else if case, i get an axception for "a as null".
This is my code (where i get an exception on the else if linq line saying that "a" is null):
//Arrays
ClienteModel[] MemoryClienti = new ClienteModel[19];
OrdineModel[] MemoryOrdini = new OrdineModel[19];
//Aggiungi
public bool CreateCliente(ClienteModel model)
{
if (MemoryClienti[0] == null)
{
int defaultID = 0;
int defaultIndex = 0;
model.IDCliente = defaultID;
MemoryClienti[defaultIndex] = model;
}
else if (MemoryClienti[0]!=null)
{
var maxID = MemoryClienti.Max(a => a.IDCliente);
model.IDCliente = maxID++;
MemoryClienti[maxID++] = model;
}
return true;
}
This is the code of the form click:
//Aggiungi Cliente
private void aggiungiClienteButton_Click(object sender, EventArgs e)
{
clienteModel.Cognome = cognomeTextBox.Text;
clienteModel.Nome = nomeTextBox.Text;
clienteModel.Indirizzo = indirizzoTextbox.Text;
dbMemoryManager.CreateCliente(clienteModel);
MessageBox.Show("Cliente aggiunto correttamente.");
cognomeTextBox.Text = String.Empty;
nomeTextBox.Text = String.Empty;
indirizzoTextbox.Text = String.Empty;
}
This is the ClienteModel class:
public class ClienteModel
{
public int IDCliente { get; set; }
public string Cognome { get; set; }
public string Nome { get; set; }
public string Indirizzo { get; set; }
}
Wouldn't the following code achieve what you are trying to do?
ClienteModel[] MemoryClienti = new ClienteModel[19];
OrdineModel[] MemoryOrdini = new OrdineModel[19];
int maxID = 0; /* 0 is not a valid ID. IDs start from 1 */
//Aggiungi
public bool CreateCliente(ClienteModel model)
{
if(maxID <= MemoryClienti.Length) {
MemoryClienti[maxID++] = model; // copy the reference
model.IDCliente = maxID; // update the object's ID
return true;
} else {
return false; // can't add. array is full
}
}
If you are also doing deletions, you are better off using a List as others have also suggested.

Updating child items in List updates all Items in C#

Here are my list :
public class PayRateDaysModel
{
public string day_name { get; set; }
public List<RateList> multiplier { get; set; }
}
public class RateList
{
public double start_after { get; set; }
public double rate_multiplier { get; set; }
}
when I'm trying to update any value in multiplier of PayRateDaysModel then its updating all multiplier values of PayRateDaysModel. Iw ant to update only current item. Below is my code :
var dayExists = daysModel.Where(x => x.day_name == day_name).FirstOrDefault();
if(dayExists==null)
{
PayRateDaysModel days = new PayRateDaysModel();
days.day_name = day_name;
days.multiplier = rate_list;
daysModel.Add(days);
}
else
{
//update
dayExists.day_name = "abc";
dayExists.multiplier.FirstOrDefault().rate_multiplier = 1;
}
Based on what you have shown to us I would think that you are creating the rate_list somewhere above like rate_list = new RateList(…) and you are setting this to all of your days in days.multiplier = rate_list;. Since you did not recreate that rate_list for every element, any time you change it in one of your dayExists you will change it for all the others as well.
So you should do something like this days.multiplier = new RateList(…);

Entity Framework - Accessing Model classes that have foreign/navigation key to themselves

I have the following Model, which can be a child of the same type, and/or have many children of the same type.
public class SystemGroupModel
{
public int Id { get; set; }
public int? ParentSystemGroupModelId { get; set; }
[ForeignKey("ParentSystemGroupModelId")]
public virtual SystemGroupModel ParentSystemGroupModel { get; set; }
[InverseProperty("ParentSystemGroupModel")]
public virtual ICollection<SystemGroupModel> SubSystemGroupModels { get; set; }
}
How can I get a specific SystemGroupModel by Id that is at an unknown depth, and include all of it's children, grandchildren, great-grandchildren, etc.?
This appears to be working, which means it will build a list of any given parent, as well as children at an unlimited depth of grandchildren, great-grandchildren, and so on.
Is this the right approach?
First I created this new class
public class SystemGroupWorkingClass
{
public SystemGroupModel SystemGroupModel { get; set; }
public bool WasChecked { get; set; }
}
Then I added this code
EDIT: Updated to include the loop that builds List<SystemGroupWorkingClass>
List<SystemGroupWorkingClass> tempListSystemGroups = new List<SystemGroupWorkingClass>();
//Get the SystemGroups that were selected in the View via checkbox
foreach (var systemGroupVM in viewModel.SystemGroups)
{
if (systemGroupVM.Selected == true)
{
SystemGroupModel systemGroupModel = await db.SystemGroupModels.FindAsync(systemGroupVM.Id);
SystemGroupWorkingClass systemGroupWorkingClass = new SystemGroupWorkingClass();
systemGroupWorkingClass.SystemGroupModel = systemGroupModel;
systemGroupWorkingClass.WasChecked = false;
systemGroupWorkingClass.Selected = true;
//Make sure tempListSystemGroups does not already have this systemGroupWorkingClass object
var alreadyAddedCheck = tempListSystemGroups
.FirstOrDefault(s => s.SystemGroupModel.Id == systemGroupVM.Id);
if (alreadyAddedCheck == null)
{
tempListSystemGroups.Add(systemGroupWorkingClass);
}
}
}
for (int i = 0; i < tempListSystemGroups.Count; i++)
{
if (tempListSystemGroups[i].WasChecked == false)
{
SystemGroupModel systemGroupModel2 = await db.SystemGroupModels.FindAsync(tempListSystemGroups[i].SystemGroupModel.Id);
//Get the children, if there are any, for the current parent
var subSystemGroupModels = systemGroupModel2.SubSystemGroupModels
.ToList();
//Loop through the children and add to tempListSystemGroups
//The children are added to tempListSystemGroups as it is being iterated over
foreach (var subSystemGroupModel in subSystemGroupModels)
{
SystemGroupModel newSystemGroupModel = await db.SystemGroupModels.FindAsync(subSystemGroupModel.Id);
SystemGroupWorkingClass subSystemGroupWorkingClass = new SystemGroupWorkingClass();
subSystemGroupWorkingClass.SystemGroupModel = newSystemGroupModel;
subSystemGroupWorkingClass.WasChecked = false;
tempListSystemGroups.Add(subSystemGroupWorkingClass);
}
}
//Mark the parent as having been checked for children
tempListSystemGroups[i].WasChecked = true;
}

How to have multiple lists in an Entity of the same type?

I have an entity that needs to have multiple lists of another entity. Each of these lists will be comprised of the same type of entity however, and this seems to confuse the framework. I've read this question:
Multiple collections of same type in entity framework
and followed the suggestion of differentiating the different lists by inheriting different types for each of the lists of items. This does not seem to do anything though.
The error I get is:
Exception:Thrown: "Invalid column name 'ApprovalStage_Id1'.
Invalid column name 'ApprovalStage_Id2'.
Invalid column name 'ApprovalStage_Id3'." (System.Data.SqlClient.SqlException)
A System.Data.SqlClient.SqlException was thrown: "Invalid column name 'ApprovalStage_Id1'.
Invalid column name 'ApprovalStage_Id2'.
Invalid column name 'ApprovalStage_Id3'."
Time: 2/9/2015 3:22:05 PM
Thread:Worker Thread[11116]
And here are my entities. It's a bit dense, but basically the main object is ComplexApprovalProcess, which has some number of ApprovalStages in a single list (that's all fine). The problem comes in with the fact that each ApprovalStage has three lists of Approver, 'Approvers', 'Viewers', and 'Advisors'. When Entity tries to save these entities it throws the error above. Like I said, I tried differentiating these by inheriting three other classes from Approver, so as you can see they are now collections of ReqApprover, Advisor and Viewer, but it still throws the error above just like it did before. Any ideas why Entity is getting confused on this? Each Approver entity should just have a reference back to the ApprovalStage it belongs to, but Entity seems to think it should be referencing three different ApprovalStages and tries to dynamically find those columns, which don't exist. Thanks.
The Entities:
public class ComplexApprovalProcess
{
[Key]
public long Id { get; set; }
[InverseProperty("ApprovalProcessId")]
public List<ApprovalStage> Stages { get; set; }
[ForeignKey("Form")]
public long FormId { get; set; }
public int CurrentStage { get; set; }
public FormBase Form { get; set; }
bool Approved { get; set; }
bool Denied { get; set; }
private bool CheckCompleted() {
foreach (ApprovalStage stage in this.Stages)
{
if (stage.Completed == false)
{
//if any stage is incomplete, the process is not complete
return false;
}
}
//no stages incomplete means all stages complete
return true;
}
private bool AdvanceStage()
{
//check the completion condition of the current stage, if completed, advance to next stage
ApprovalStage current = Stages.Where(m => m.StageOrder == this.CurrentStage).FirstOrDefault();
if (current != null)
{
//check if stage is completed
if (current.CheckCompletion())
{
//check if stage is approved
if (current.Approved)
{
//check if process contains additional stages
if (this.Stages.Count > this.CurrentStage)
{
//Move to next stage
this.CurrentStage += 1;
ApprovalStage next = Stages.Where(m => m.StageOrder == this.CurrentStage).FirstOrDefault();
if (next != null)
{
next.StartStage();
}
else
{
throw new Exception("Huh?");
}
}
}
}
}
else
{
throw new Exception("Wut");
}
return false;
}
public static ComplexApprovalProcess CreateCheckRequestApprovalProcess(FormBase form)
{
UsersModel user = null;
ComplexApprovalProcess process = new ComplexApprovalProcess();
using (TechnologyProjectPlanContext db = new TechnologyProjectPlanContext())
{
int id = SessionVar.Get<int>(SessionVar.USERID);
user = db.UsersModels.Where(m => m.Id == id).FirstOrDefault();
}
process.Form = form;
ApprovalStage InitialReview = new ApprovalStage();
InitialReview.StageOrder = 1;
InitialReview.Approvers = new List<ReqApprover>();
InitialReview.Advisors = new List<Advisor>();
InitialReview.Viewers = new List<Viewer>();
InitialReview.Form = form;
InitialReview.ApprovalProcess = process;
InitialReview.Approvers.Add(new ReqApprover(user, form, InitialReview));
InitialReview.Advisors.Add(new Advisor(user, form, InitialReview));
InitialReview.Viewers.Add(new Viewer(user, form, InitialReview));
InitialReview.StageName = "Initial Review";
ApprovalStage MiddleApproval = new ApprovalStage();
MiddleApproval.StageOrder = 2;
MiddleApproval.Approvers = new List<ReqApprover>();
MiddleApproval.Advisors = new List<Advisor>();
MiddleApproval.Viewers = new List<Viewer>();
MiddleApproval.Form = form;
MiddleApproval.ApprovalProcess = process;
MiddleApproval.Approvers.Add(new ReqApprover(user, form, MiddleApproval));
MiddleApproval.Advisors.Add(new Advisor(user, form, MiddleApproval));
MiddleApproval.Viewers.Add(new Viewer(user, form, MiddleApproval));
MiddleApproval.StageName = "Middle Approval";
ApprovalStage FinalApproval = new ApprovalStage();
FinalApproval.StageOrder = 3;
FinalApproval.Approvers = new List<ReqApprover>();
FinalApproval.Advisors = new List<Advisor>();
FinalApproval.Viewers = new List<Viewer>();
FinalApproval.Form = form;
FinalApproval.ApprovalProcess = process;
FinalApproval.Approvers.Add(new ReqApprover(user, form, FinalApproval));
FinalApproval.Advisors.Add(new Advisor(user, form, FinalApproval));
FinalApproval.Viewers.Add(new Viewer(user, form, FinalApproval));
FinalApproval.StageName = "Final Approval";
process.Stages = new List<ApprovalStage>();
process.Stages.AddRange(new ApprovalStage[] { InitialReview, MiddleApproval, FinalApproval });
//set default values
process.Approved = false;
process.Denied = false;
process.CurrentStage = 1;
process.Stages[0].StartStage();
return process;
}
public void SaveToDb()
{
//make sure we have at least one stage and either a form reference (new form) or form id (old form) before moving forward
if ((Stages != null && Stages.Count > 0) && (Form != null || FormId > 0))
{
using (TechnologyProjectPlanContext db = new TechnologyProjectPlanContext())
{
//first we have to save the process to get an Id
//copy stages out so we can save without the fuss
List<ApprovalStage> stages = this.Stages;
this.Stages = null;
db.ComplexApprovalProcesses.Add(this);
db.SaveChanges();
//'this' now has an Id
//ok let's work it out from the bottom to the top, first separate out approvers from stages and save:
foreach (ApprovalStage stage in stages)
{
ICollection<ReqApprover> approvers = stage.Approvers;
ICollection<Advisor> advisors = stage.Advisors;
ICollection<Viewer> viewers = stage.Viewers;
stage.FormId = stage.Form.Id;
stage.Form = null;
stage.Approvers = null;
stage.Advisors = null;
stage.Viewers = null;
stage.ApprovalProcessId = this.Id;
db.ApprovalStages.Add(stage);
db.SaveChanges();
//stage now has an id;
//iterate through each set of approvers and save
foreach (Approver approver in approvers)
{
approver.FormId = stage.FormId;
approver.UserId = approver.User.Id;
approver.ApprovalStage_Id = stage.Id;
approver.Form = null;
approver.User = null;
approver.Stage = null;
db.Approvers.Add(approver);
db.SaveChanges();
}
foreach (Advisor approver in advisors)
{
approver.FormId = stage.FormId;
approver.UserId = approver.User.Id;
approver.ApprovalStage_Id = stage.Id;
approver.Form = null;
approver.User = null;
approver.Stage = null;
db.Approvers.Add(approver);
}
foreach (Viewer approver in viewers)
{
approver.FormId = stage.FormId;
approver.UserId = approver.User.Id;
approver.ApprovalStage_Id = stage.Id;
approver.Form = null;
approver.User = null;
approver.Stage = null;
db.Approvers.Add(approver);
}
db.SaveChanges();
}
}
}
}
}
public class ApprovalStage
{
//Each stage requires at least one approver
[Key]
public long Id { get; set; }
[ForeignKey("ApprovalProcess")]
public long ApprovalProcessId { get; set; }
[ForeignKey("Form")]
public long FormId { get; set; }
public FormBase Form { get; set; }
public ComplexApprovalProcess ApprovalProcess { get; set; }
public ICollection<ReqApprover> Approvers { get; set; } //These users are required to approve before the form can move to the next stage.
public ICollection<Advisor> Advisors { get; set; } //These users can see the form and approve at this stage, but they are not required.
public ICollection<Viewer> Viewers { get; set; } //These users can see the form, but cannot approve
public string StageName { get; set; } //Name of stage e.g. Review, Final Approval, etc. Gives a custom feel?
public int StageOrder { get; set; }
public bool Completed { get; set; }
public bool Approved { get; set; }
public bool Denied { get; set; }
public bool CanAbstain { get; set; }
public ApprovalStage()
{
this.Approved = false;
this.Denied = false;
this.Completed = false;
}
}
public class Approver
{
[Key]
public long Id { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public UsersModel User { get; set; }
[ForeignKey("Form")]
public long FormId { get; set; }
public FormBase Form { get; set; }
[ForeignKey("Stage")]
public long ApprovalStage_Id { get; set; }
public ApprovalStage Stage { get; set; }
public bool Approved { get; set; }
public bool Denied { get; set; }
public bool Abstain { get; set; }
public Approver() { }
public Approver(UsersModel user, FormBase form, ApprovalStage stage)
{
this.Stage = stage;
this.User = user;
this.Approved = false;
this.Denied = false;
this.Abstain = false;
}
}
Ok so I figured out the error I was making. I didn't think about the fact that once placed into the database, the different lists of the same object type would have no knowledge of which list they originated from. That is, an Approver, Advisor, and Viewer would all look the same to the framework after they've been inserted. I kept running into errors with the inheritance of Approvers, so I simply made 3 different classes and tables and copied the design to each of them and it works perfectly. Can't believe I spent so much time on this...

select child object collection with lambdas

I have the following class objects:
public class VacancyCategory
{
public int ID { get; set; }
public string Text { get; set; }
public IList<VacancySubCategory> SubCategories { get; set; }
}
public class VacancySubCategory
{
public int ID { get; set; }
public string Text { get; set; }
public VacancyCategory Category { get; set; }
public IList<Vacancy> Vacancies { get; set; }
}
public class Vacancy : IBusinessObject
{
public int ID { get; set; }
public string Title { get; set; }
public VacancySubCategory SubCategory { get; set; }
public string Body { get; set; }
public VacancyWorkType WorkType { get; set; }
public string Salary { get; set; }
public DateTime? AppsClosingDate { get; set; }
public bool Active { get; set; }
}
...so in a test repository im creating test data like so:
private IList<VacancyCategory> GetVacancyCategoriesWithAllChildCollections()
{
IList<VacancyCategory> vacancyCategories = new List<VacancyCategory>();
int cCounter = 0;
int scCounter = 0;
int vCounter = 0;
for (int i = 1; i <= 3; i++)
{
VacancyCategory vc = new VacancyCategory();
vc.ID = ++cCounter;
vc.Text = "VacancyCategory" + i.ToString();
for (int j = 1; j <= 3; j++)
{
VacancySubCategory vsc = new VacancySubCategory();
vsc.ID = ++scCounter;
vsc.Text = "VacancySubCategory" + scCounter.ToString();
vsc.Category = vc;
for (int k = 1; k <= 2; k++)
{
Vacancy v = new Vacancy();
v.ID = ++vCounter;
v.Title = "Vacancy" + vCounter.ToString();
v.Body = "VacancyBody" + vCounter.ToString();
v.Active = vCounter >= 16 ? false : true;
v.WorkType = this._workTypes.Single(wt => wt.ID == k);
v.Salary = vCounter <= 7 ? "SR " + (vCounter * 1000).ToString() : "";
v.AppsClosingDate = (vCounter >= 3 & vCounter <= 13) ? (new DateTime(2009, 3, vCounter)) : (DateTime?)null;
v.SubCategory = vsc;
if (vsc.Vacancies == null)
vsc.Vacancies = new List<Vacancy>();
vsc.Vacancies.Add(v);
}
if (vc.SubCategories == null)
vc.SubCategories = new List<VacancySubCategory>();
vc.SubCategories.Add(vsc);
}
vacancyCategories.Add(vc);
}
return vacancyCategories;
}
..so now i have some good test data. the object tree / chained objects are important to me.
so i'd like to return the individual object collections from this tree when desired. for example, if i wanted the whole tree, i can just return the VacancyCategory list with all the child objects - great. but now i want to return just the VacancySubCaregory items (all 9 of them). this would be my public method to the test repository:
public IQueryable<VacancySubCategory> GetVacancySubCategories()
{
throw new NotImplementedException("write gen code");
}
.. obviously without the exception. i have a member field called _categories that contains the results from the GetVacancyCategoriesWithAllChildCollections method. so i've been trying stuff like
this._categories.Select( ......
..but i cant seem to return a list of VacancySubCategory objects. i seem to always be selecting the root collection (ie. a result set of VacancyCategory objects). What am i doing wrong? im sure its simple... but its driving me nuts!
EDIT
thanx matt.
your suggestion led me to this:
public IQueryable<VacancySubCategory> GetVacancySubCategories()
{
return this._categories.SelectMany(c => c.SubCategories).AsQueryable<VacancySubCategory>();
}
..which works great. you're a champ
Try:
return this._categories.SelectMany(c => c.SubCategories);
This should work.
var query = from vc in GetVacancyCategoriesWithAllChildCollections()
from vcs in vc.SubCategories
select vcs

Categories