How to add nodes to my Child/Parent object? - c#

I have an object below:
public class SubjectCategory : BaseModel
{
public decimal ParentSubjectCategoryId { get; set; }
public bool IsEnable { get; set; }
public virtual List<Subject>? Subjects { get; set; }
public virtual List<SubjectCategory>? ChildrenCategoris { get; set; }
public virtual SubjectCategory? ParentCategory { get; set; }
}
I get lists of subjectCategories from database (Picture Below).
I wrote a method that it adds ChildrenCategoris inside the categories which their ParentSubjectCategory Id is NULL or 0, but the problem is it is only works for the first level of tree!
public List<SubjectCategory> GetAllSubjectCategories()
{
var res = _subjectCategoryRepository.Select(new SubjectCategory {}).ToList();
List<SubjectCategory> newSubjectCategory = new List<SubjectCategory>();
foreach (var item in res)
{
if(item.ParentSubjectCategoryId != 0)
{
var a = newSubjectCategory.Where(sc => sc.Id ==
item.ParentSubjectCategoryId).FirstOrDefault();
if(a.ChildrenCategoris == null)
{
newSubjectCategory.Where(sc => sc.Id ==
item.ParentSubjectCategoryId).FirstOrDefault().ChildrenCategoris = new List<SubjectCategory>() { item};
}
else
{
newSubjectCategory.Where(sc => sc.Id == item.ParentSubjectCategoryId).FirstOrDefault().ChildrenCategoris.Add(item);
}
}
else
{
newSubjectCategory.Add(item);
}
}
return res;
}
But every child can have many ChildrenCategoris and their children can have many ChildrenCategoris as well and again.
the loop count is unknown.
how can I have a list with multiple children with C# ?

Related

Tree hierarchy setting flag true if child exists C#

I am trying to set issetting flag to true , if child exists for a
parent.
//Class file
public class EopsModule
{
public int ID { get; set; }
public string ModuleCode { get; set; }
public string Description { get; set; }
public bool IsDefaultModule { get; set; }
public int? ParentID { get; set; }
public bool IsSetting { get; set; }
public List<EopsModule> Children { get; set; }
}
public IResponseResult GetApplicationSettingWithModule()
{
IResponseResult responseResult = new ResponseResult();
dynamic dynamic = new ExpandoObject();
try
{
var settingsDetails = _databaseManager.GetMultipleDataByJson(DatabaseStoredProcedures.spGetAllApplicationSetting.ToString()).Result;
var oObjectDeserializeObject = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(settingsDetails);
//get here all EopsModule in List<EopsModule>
var moduleTreeHierarchy = _eopsModuleManager.GetAllEopsModuleWithHierarchy().Result;
dynamic.settingsDetails = oObjectDeserializeObject;
dynamic.moduleTreeHierarchy = moduleTreeHierarchy;
string oModuleCode = string.Empty;
foreach (var item in oObjectDeserializeObject)
{
oModuleCode = item.moduleCode;
moduleTreeHierarchy.
Where(x => x.ModuleCode == oModuleCode).ToList().ForEach(x =>
{
x.IsSetting = true;
});
}
responseResult = Helper.Response.ResponseModel(dynamic, true, Helper.Constants.ApiSuccess, HttpStatusCode.OK, true);
}
catch (Exception)
{
responseResult = Helper.Response.ResponseModel(null, false, Helper.Constants.ApiFailure, HttpStatusCode.BadRequest, true);
}
return responseResult;
}
The loop i am iterating is working for parent level but , its not updating the value for child values,
wondering if it can be achieved by recursive function.
Please find the output with existing code :
Have you tried something like this? I did not pass it through compiler but you'll get the idea.
public UpdateModuleAndChildren(Module moduleTreeHierarchy) {
if(moduleTreeHierarchy.children != null && moduleTreeHierarchy.children.Count() > 0) {
moduleTreeHierarchy.children.forEach(x => { this.UpdateModuleAndChildren(x) });
module.IsSetting = true;
}
}
Let me know if it helps.
In your code you will just call this.UpdateModuleAndChildren(moduleTreeHierarchy)

Linq query for the Multi list in singe out put

i have table looks like below
ID | Reason | PrID
-----------------
1 abc null
2 dhe null
3 aerc 1
4 dwes 2
5 adfje 1
i have class
public class Reason
{
public int Id { get; set; }
public string Reson{ get; set; }
public List<SecondryReason> SecReason{ get; set; }
public int? PrimaryId { get; set; }
}
public class SecondryReason
{
public int Id { get; set; }
public string Reason { get; set; }
public int PrimaryReasonId { get; set; }
}
I want this to be displayed in hierarchy level
if the prid is Null need to treat this as the parent remaining all child
i am trying Linq and unable to achieve this
Suggest me how to do this in an easy way in linq
So: You have a list/enumerable of type , whereof the SecReason List property is null. Then, using linq you want a list, were the only the "root" reasons remain, but the Sub-reasons got put in the lists, but as type SecondaryReason?
If so, I found this way to do it (linq and foreach):
static IEnumerable<Reason> GetReasonsGrouped(List<Reason> reasons)
{
var result = reasons.Where(x => x.PrimaryId == null);
foreach (var item in result)
{
item.SecReason = reasons.Where(x => x.PrimaryId == item.Id)
.Select(x => new SecondryReason()
{ Id = x.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = item.Id
})
.ToList();
}
return result;
}
Or just linq, but harder to read:
var result = reasons.Where(x => x.PrimaryId == null)
.Select(x =>
{
x.SecReason = reasons.Where(r => x.PrimaryId == x.Id)
.Select(r => new SecondryReason()
{
Id = r.Id,
ReasonName = x.ReasonName,
PrimaryReasonId = x.Id
})
.ToList();
return x;
});
Not sure if linq will be the best solution, here is my proposed changes and method to get an Hierarchy type:
public class Reason
{
public int Id { get; set; }
public string Reson { get; set; }
public List<Reason> SecReason { get; set; }
public int? PrimaryId { get; set; }
//Adds child to this reason object or any of its children/grandchildren/... identified by primaryId
public bool addChild(int primaryId, Reason newChildNode)
{
if (Id.Equals(primaryId))
{
addChild(newChildNode);
return true;
}
else
{
if (SecReason != null)
{
foreach (Reason child in SecReason)
{
if (child.addChild(primaryId, newChildNode))
return true;
}
}
}
return false;
}
public void addChild(Reason child)
{
if (SecReason == null) SecReason = new List<Reason>();
SecReason.Add(child);
}
}
private List<Reason> GetReasonsHierarchy(List<Reason> reasons)
{
List<Reason> reasonsHierarchy = new List<Reason>();
foreach (Reason r in reasons)
{
bool parentFound = false;
if (r.PrimaryId != null)
{
foreach (Reason parent in reasonsHierarchy)
{
parentFound = parent.addChild(r.PrimaryId.Value, r);
if (parentFound) break;
}
}
if (!parentFound) reasonsHierarchy.Add(r);
}
return reasonsHierarchy;
}

Columns.Add that uses lambda expression

I am working with some code that uses Columns.Add in conjunction with a lambda expression and would like to understand why/how it works. Here is a code snippet:
public ReportGrid(List<ReportRowDataContract> items)
: base(items)
{
if (items[0].ReportData1 != null)
{
if (items[0].ReportData1.DecimalValue != null)
{
Columns.Add(m => m.ReportData1.DecimalValue).Titled(items[0].ReportData1.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData1.DisplayFormat)) ? Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString("N") : Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (items[0].ReportData1.SumValue || items[0].ReportData1.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData1.Name,
AvgValue = items[0].ReportData1.AvgValue,
DecimalValue = 0
});
}
}
else if (items[0].ReportData1.IntValue != null)
{
Columns.Add(m => m.ReportData1.IntValue).Titled(items[0].ReportData1.Name);
if (items[0].ReportData1.SumValue || items[0].ReportData1.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData1.Name,
AvgValue = items[0].ReportData1.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData1.StringValue).Titled(items[0].ReportData1.Name);
}
}
if (items[0].ReportData2 != null)
{
if (items[0].ReportData2.DecimalValue != null)
{
Columns.Add(m => m.ReportData2.DecimalValue).Titled(items[0].ReportData2.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData2.DisplayFormat)) ? Convert.ToDecimal(#m.ReportData2.DecimalValue).ToString("N") : Convert.ToDecimal(#m.ReportData2.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (items[0].ReportData2.SumValue || items[0].ReportData2.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData2.Name,
AvgValue = items[0].ReportData2.AvgValue,
DecimalValue = 0
});
}
}
else if (items[0].ReportData2.IntValue != null)
{
Columns.Add(m => m.ReportData2.IntValue).Titled(items[0].ReportData2.Name);
if (items[0].ReportData2.SumValue || items[0].ReportData2.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = items[0].ReportData2.Name,
AvgValue = items[0].ReportData2.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData2.StringValue).Titled(items[0].ReportData2.Name);
}
}
This method consists of code that repeats itself out to ReportData6, changing only the ReportData field name with each repetition.
Here is the ReportRowDataContract class:
public class ReportRowDataContract
{
public ReportDataDataContract ReportData1 { get; set; }
public ReportDataDataContract ReportData2 { get; set; }
public ReportDataDataContract ReportData3 { get; set; }
public ReportDataDataContract ReportData4 { get; set; }
public ReportDataDataContract ReportData5 { get; set; }
public ReportDataDataContract ReportData6 { get; set; }
// an indexed property - for accessing report data fields by index
public ReportDataDataContract this[int i]
{
get
{
return new ReportDataDataContract[]
{
ReportData1,
ReportData2,
ReportData3,
ReportData4,
ReportData5,
ReportData6
}[i];
}
}
public int GetReportDataFieldCount()
{
return 6;
}
}
Unfortunately, I cannot change the structure of this class so I'm trying to convert the first code block into a method that loops. However, I'm stuck on what the Columns.Add is actually adding to.
Here is my code thus far:
public ReportGrid(List<ReportRowDataContract> items)
: base(items)
{
// get count of how many fields exist in ReportRowDataContract
int reportDataFieldCount = (new ReportRowDataContract()).GetReportDataFieldCount();
// create columns for grid for each field in ReportRowDataContract
//foreach (ReportRowDataContract item in items)
//{
int i = 0;
while (i < reportDataFieldCount)
{
AddGridColumn(items[0][i]);
i++;
}
//}
}
private void AddGridColumn(ReportDataDataContract reportColumn)
{
if (reportColumn != null)
{
if (reportColumn.DecimalValue != null)
{
Columns.Add(m => m.ReportData1.DecimalValue).Titled(reportColumn.Name).Encoded(false).
Sanitized(false).RenderValueAs(
m => (string.IsNullOrEmpty(#m.ReportData1.DisplayFormat)) ?
Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString("N") :
Convert.ToDecimal(#m.ReportData1.DecimalValue).ToString(#m.ReportData1.DisplayFormat));
if (reportColumn.SumValue || reportColumn.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = reportColumn.Name,
AvgValue = reportColumn.AvgValue,
DecimalValue = 0
});
}
}
else if (reportColumn.IntValue != null)
{
Columns.Add(m => m.ReportData1.IntValue).Titled(reportColumn.Name);
if (reportColumn.SumValue || reportColumn.AvgValue)
{
displaySummary = true;
SummaryData.Add(
new ReportDataDataContract
{
Name = reportColumn.Name,
AvgValue = reportColumn.AvgValue,
IntValue = 0
});
}
}
else
{
Columns.Add(m => m.ReportData1.StringValue).Titled(reportColumn.Name);
}
}
}
In the AddGridColumn method the part that is problematic is m => m.ReportData1. It's not dynamic so the first loop through is fine but on the second loop through an exception is thrown: System.ArgumentException {"Column 'ReportData1.StringValue' already exist in the grid"}. I know the m.ReportData1 needs to be changed or the approach modified...just don't know how to go about it.
Edit #1: Disabled the foreach per VitezslavSimon's insight. The same exception message is being thrown.
Edit #2: Base class of grid (I think).
public class Grid<T> : GridBase<T>, IGrid where T : class
{
public Grid(IEnumerable<T> items);
public Grid(IQueryable<T> items);
public IGridColumnCollection<T> Columns { get; }
public bool DefaultFilteringEnabled { get; set; }
public bool DefaultSortEnabled { get; set; }
public virtual int DisplayingItemsCount { get; }
public bool EnablePaging { get; set; }
public string Language { get; set; }
public IGridPager Pager { get; set; }
public GridRenderOptions RenderOptions { get; set; }
public ISanitizer Sanitizer { get; set; }
public override IGridSettingsProvider Settings { get; set; }
public virtual void AutoGenerateColumns();
protected internal virtual IEnumerable<T> GetItemsToDisplay();
}
It seems you need to add columns only once there. Try to help it by declaring a flag there. It also depends how your grid component is working behind.
Your code with draft of proposed change:
public ReportGrid(List<ReportRowDataContract> items)
: base(items)
{
// get count of how many fields exist in ReportRowDataContract
int reportDataFieldCount = (new ReportRowDataContract()).GetReportDataFieldCount();
// create columns for grid for each field in ReportRowDataContract
bool flag = true;
foreach (ReportRowDataContract item in items)
{
int i = 0;
if (flag) {
while (i < reportDataFieldCount)
{
AddGridColumn(items[0][i]);
i++;
}
flag = false;
}
}
}

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;
}

Compare collection with objects and copy the differences

I have a Collection:
Collection<DateOfValues> collectionDateOfValues;
...
I get a DateOfValues Instance - lets say dateOfValuesNew and want to iterate over the collection and overwrite only values that are different.
public class DateOfValues
{
{
this.Values = new Collection<SomeValue>();
}
public int id { get; set;}
public DateTime Start { get; set; }
public Collection<SomeValue> Values;
}
public class SomeValue
{
public int Id { get; set; }
public DateOfValues Date { get; set; }
public string Status { get; set; }
publi decimal StatusNumber { get; set; }
}
What I have done:
if (dateOfValuesNew != null)
{
foreach (var dateOfValues in collectionDateOfValues)
{
if (dateOfValues.Id == dateOfValuesNew.Id)
{
// Here Im sure to find the dateOfValues Instance I will work with.
}
}
}
But If I want to compare dateOfValues with dateOfValuesNew with foreach it is ugly and unreadable.
Is there any better way to do it?
The Start of DateOfValues can be changed. It is the easiest part - cause I can simply overwrite it.
The hard part is to compare SomeValue Collection. Every SomeValue can have changed Date and Status - this can be solved with overwriting too.
But SomeValue Collection can become new SomeValue or it can be deleted.
For example dateOfValues has 3 SomeValue in SomeValue Collection and dateOfValuesNew will have 4 or 2.
A bit tricky but this works:
class Program
{
static void Main(string[] args)
{
// Test
DateOfValues dov1 = new DateOfValues { Id = 1, Start = new DateTime(2011, 12, 01) };
dov1.AddSomeValue(1,"OK",2);
dov1.AddSomeValue(2,"Not OK",3);
dov1.AddSomeValue(3,"Not OK",4);
dov1.AddSomeValue(4,"Additional dov1",5);
DateOfValues dov2 = new DateOfValues { Id = 1, Start = new DateTime(2011, 12, 02) };
dov2.AddSomeValue(1, "OK", 2);
dov2.AddSomeValue(2, "Not OK", 4);
dov2.AddSomeValue(3, "OK", 1);
dov2.AddSomeValue(6, "Additional dov2", 15);
foreach (Tuple<SomeValue,SomeValue> difference in dov1.GetDifference(dov2))
{
if (difference.Item1 != null)
{
Console.WriteLine("Item1: Id:{0}; Status:{1}; Status Number:{2}",
difference.Item1.Id, difference.Item1.Status, difference.Item1.StatusNumber);
}
if (difference.Item2 != null)
{
Console.WriteLine("Item2: Id:{0}; Status:{1}; Status Number:{2}",
difference.Item2.Id, difference.Item2.Status, difference.Item2.StatusNumber);
}
Console.WriteLine("-------------------------------------------");
}
}
}
public class DateOfValues
{
public DateOfValues()
{
Values = new Collection<SomeValue>();
}
public int Id { get; set; }
public DateTime Start { get; set; }
public Collection<SomeValue> Values;
public void AddSomeValue(int id, string status, decimal statusNumber)
{
Values.Add(new SomeValue{Date = this,Id = id,Status = status,StatusNumber = statusNumber});
}
public IEnumerable<Tuple<SomeValue, SomeValue>> GetDifference(DateOfValues other)
{
IEnumerable<SomeValue> notMatching = Values.Where(v => !other.Values.Any(o => v.Equals(o)))
.Union(other.Values.Where(v=> !Values.Any(o=> v.Equals(o)))).Distinct();
return notMatching
.GroupBy(x => x.Id)
.Select(x =>
new Tuple<SomeValue, SomeValue>(
x.FirstOrDefault(y => y.Date == this), x.FirstOrDefault(y => y.Date == other)));
}
}
public class SomeValue : IEquatable<SomeValue>
{
public int Id { get; set; }
public DateOfValues Date { get; set; }
public string Status { get; set; }
public decimal StatusNumber { get; set; }
public bool Equals(SomeValue other)
{
return other.Id == Id && other.Status == Status && other.StatusNumber == StatusNumber;
}
}
Output:
Item1: Id:2; Status:Not OK; Status Number:3
Item2: Id:2; Status:Not OK; Status Number:4
-------------------------------------------
Item1: Id:3; Status:Not OK; Status Number:4
Item2: Id:3; Status:OK; Status Number:1
-------------------------------------------
Item1: Id:4; Status:Additional dov1; Status Number:5
-------------------------------------------
Item2: Id:6; Status:Additional dov2; Status Number:15
-------------------------------------------
Edit
Alternative you could use an EqualityComparer:
public class DateOfValues
{
public DateOfValues()
{
Values = new Collection<SomeValue>();
}
public int Id { get; set; }
public DateTime Start { get; set; }
public Collection<SomeValue> Values;
public void AddSomeValue(int id, string status, decimal statusNumber)
{
Values.Add(new SomeValue { Date = this, Id = id, Status = status, StatusNumber = statusNumber });
}
public IEnumerable<Tuple<SomeValue, SomeValue>> GetDifference(DateOfValues other)
{
var notMatching = Values.Except(other.Values, new SomeValueComparer())
.Union(other.Values.Except(Values,new SomeValueComparer()));
return notMatching
.GroupBy(x => x.Id)
.Select(x =>
new Tuple<SomeValue, SomeValue>(
x.FirstOrDefault(y => y.Date == this), x.FirstOrDefault(y => y.Date == other)));
}
}
public class SomeValueComparer : IEqualityComparer<SomeValue>
{
public bool Equals(SomeValue x, SomeValue y)
{
return
x.Id == y.Id &&
x.Status == y.Status &&
x.StatusNumber == y.StatusNumber;
}
public int GetHashCode(SomeValue obj)
{
return obj.GetHashCode();
}
}
public class SomeValue
{
public int Id { get; set; }
public DateOfValues Date { get; set; }
public string Status { get; set; }
public decimal StatusNumber { get; set; }
public override int GetHashCode()
{
return string.Format("{0}{1}{2}",Id,Status,StatusNumber).GetHashCode();
// or a better method to get a hashcode
}
}

Categories