I've a class with Tree hierarchy as below. I need to set Leaf node to status "InProgress". Then All ancestors needs to have change from NotStarted to InProgress. And if all leaf nodes were Complete, it immediate parent status need to be Complete. Is there a quick way to do it other than recursively iterating multiple times?
pubic class Node
{
public string Name { get; set; }
public List<Node> Children { get; set; } = new List<Node>();
public int NodeStatus {get; set;} // 1=>NotStarted (default); 2=> InProgress; 3=>Complete
}
One simple way can be that you make a different class for child notes and notify your parent class each time when its property is changed:
Parent
public class Node
{
public string Name { get; set; }
public List<Node> Children { get; set; } = new List<Node>();
private int _nodeStatus;
// 1=>NotStarted (default); 2=> InProgress; 3=>Complete
public virtual int NodeStatus
{
get => _nodeStatus;
set
{
_nodeStatus = value;
// If started, then change all children property to 'In progress'
if(value == 2)
Children.ForEach(child => child.NodeStatus = 2);
}
}
private int CountCompleted { get; set; }
public void NotifyChildChanged(int childStatus)
{
if (childStatus == 3)
{
CountCompleted++;
if(CountCompleted == Children.Count)
// Do your stuff
}
else
{
CountCompleted--;
}
}
}
Child
public class ChildNode : Node
{
private int _nodeStatus;
public override int NodeStatus
{
get => _nodeStatus;
set
{
_nodeStatus = value;
NotifyChildChanged(value);
}
}
}
Basically, what you will have is a separate implementation of NodeStatus for Parent and Child classes and inside of their implementation you just put other needed methods like changing childs' values or notifying parent about child's complete. In the parent's case, however, you should be careful, because every time you change the parent it will change all children, so you might want to have also some kind of private bool which will be checking whether the progress has already been started or not.
P.S for simplicity, I used just methods, however, you might want to implement it through delegates and events.
Related
After looking at major pattern designs, I can't seem to make up my mind around the best to one to decouple classes in a big hierarchy system, specially were it concerns on avoiding injecting a Parent property in EVERY object along the way.
Some of the premises are:
A child might me removed from one parent and added to another.
Somewhere down the hierarchy, I need to access Parent of type X.
As mentioned before, I would like to avoid injecting a Parent (either by property or constructor) to it's children.
I have 1..1 and 1...N cardinalities.
The hierarchy from root to furthest leaf is quite extent.
If it was a small project, I would be fine with this (pseudo code):
public abstract class BaseObject()
{
public BaseObject Parent { get; set; }
}
public class RootObject() : BaseObject
{
public int Id { get; }
public ParentObject[] Parent { get; set; }
}
public class ParentObject() : BaseObject
{
public int Id { get; }
public ChildObject[] Parent { get; set; }
}
public class ChildObject() : BaseObject
{
public int Id { get; }
public void DoSomething()
{
//...navigate through Parent to get RootObject (or any other type in between that I might need)...
}
}
Can anyone point me out to the right direction?
All these requirements remind me graph data structure:
A child might me removed from one parent and added to another.
Somewhere down the hierarchy, I need to access Parent of type X.
As mentioned before, I would like to avoid injecting a Parent (either by property or constructor) to it's children.
I have 1..1 and 1...N cardinalities.
The hierarchy from root to furthest leaf is quite extent.
The easiest storage would be List<Node> where each node contains links to its predecessors and successors:
class Example
{
public List<Node> InitGraph()
{
var nodes = new Dictionary<string, Node>();
nodes.Add("Head", new Node("Head"));
nodes.Add("T1", new Node("T1"));
nodes.Add("T2", new Node("T2"));
// While that works, a method is nicer:
nodes.Add("C1");
// These two lines should really be factored out to a single method call
nodes["Head"].Successors.Add(nodes["T1"]);
nodes["T1"].Predecessors.Add(nodes["Head"]);
nodes["Head"].Successors.Add(nodes["T2"]);
nodes["T2"].Predecessors.Add(nodes["Head"]);
// Yes. Much nicer
nodes.Connect("Head", "C1");
nodes.Connect("T1", "C1");
nodes.Connect("T2", "C1");
var nodelist = new List<Node>(nodes.Values);
return nodelist;
}
}
and NodeHelper class:
public static class NodeHelper
{
public static void Add(this Dictionary<string, Node> dict, string nodename)
{
dict.Add(nodename, new Node(nodename));
}
public static void Connect(this Dictionary<string, Node> dict, string from, string to)
{
dict[ from ].Successors.Add(dict[ to ]);
dict[ to ].Predecessors.Add(dict[ from ]);
}
}
and Node class:
public class Node
{
public string Name { get; set; }
public int Coolness { get; set; }
public List<Node> Predecessors { get; set; }
public List<Node> Successors { get; set; }
public Node()
{
Coolness = 1;
}
public Node(string name) : this()
{
this.Name = name;
}
}
I'm re-asking this from a question a couple of days ago now I've whittled the problem down.
Two simple objects:
public class Parent
{
public int Id { get; set; }
public virtual Child Child { get; set; }
public string Name { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Name { get; set; }
}
I find a Parent object using a DbContext method e.g.
Parent parentToUpdate = _context.Parent.Find(1);
This object comes equipped with a populated child already, say with Id 22, generated as a System.Data.Entity.DynamicProxy
I then have a new child object which becomes a null because it wasn't found in the database, using the same DbContext:
Child newChild = _context.Child.Find(999); // returns null
I then try to overwrite the parentToUpdate object's child with the newChild object:
parentToUpdate.Child = newChild;
I expect .Child to become null - This doesn't work unless I step through the code - The parentToUpdate.Child doesn't become null!
WHY? and How can I nullify my parentToUpdate.Child object? before I do _context.SaveChanges()
Ok so thanks to the breadcrumb of lazy loading I ended up circling back on Include which I'd glossed over earlier looking for a solution.
It's as simple as changing the Find in context statement from
Parent parentToUpdate = _context.Parent.Find(1);
To
Parent parentToUpdate = _context.Parent.Include(x => x.Child).Where(x => x.Id == 1);
Suppose that we have two classes as bellow:
class Parent
{
public int ParentId {get; set;}
}
class Child : Parent
{
public int ChildId {get; set;}
}
According to the above, which line is correct and why? and also which line is not correct and why?
Parent p = new Child();
Child c = new Parent();
Update: I forget to inherit Child from Parent and I correct it!
First one is correct, second is not.
By definition, a Child will inherit all properties and methods of a Parent; however, Parent will not have all properties and/or methods of a Child, so the second statement wouldn't make sense:
class Parent
{
public int ParentId { get; set; }
public void Eat { ... }
}
class Child : Parent
{
public int ChildId { get; set; }
public void Play { ... }
}
Parent child = new Child();
child.Eat(); // this makes sense since this is common functionality
Child parent = new Parent();
parent.Play() // this does not make sense since a Parent doesn't know hot to play
I am trying to retrieve all entities of type Queried from the database where Referenced property or it's ancestor for which its label (meaning referenced.Parent.ChildLabel) is equal to some given label value exampleLabel, has an Id equal to exampleId.
I tried using:
var result = nhibernateSession
.Query<Queried>()
.Where(queried => queried.SelfReferencing.GetSelfOrAncestor("exampleLabel") == exampleId)
.ToList();
but it throws a "System.NotSupportedException", probably because it does not know how to translate the GetSelfOrAncestor into SQL.
The method GetSelfOrAncestor(string label) returns the Id of the SelfReferencing instance on which it was called or it's ancestor which meets the condition that this.Parent.ChildLabel is equal to exampleLabel, otherwise returns 0.
For example, in the following diagram if queried.SelfReferencing would point to the one at Level 2, GetSelfOrAncestor("exampleLabel") would return the Id of the object at Level 1.
http://j.mp/Xl86OP
public class Queried
{
public int Id { get; set; }
public SelfReferencing Referenced { get; set; }
}
public class SelfReferencing
{
public SelfReferencing Parent { get; set; }
private IList<SelfReferencing > children = new List<SelfReferencing >();
public virtual IList<SelfReferencing > Children
{
get
{
return children;
}
set
{
children = value;
}
}
public string ChildLabel { get; set; }
}
Any help on how to achieve this would be highly appreciated :)
To achieve what you want, I would provide a method in SelfReferencing that does the search for the label through the object graph.
Should be something like this: (Warning, not tested yet!)
public bool ContainsLabel(string label)
{
if (this.ChildLabel.Equals(label))
{
return true;
}
else
{
foreach (var child in Children)
{
return child.ContainsLabel(label);
}
}
return false;
}
You can use it then as follows:
var result = nhibernateSession
.Query<Queried>()
.Where(queried => queried.SelfReferencing.ContainsLabel("exampleLabel"))
.ToList();
Edited for more convenience in reading.
I currently have an extension Method which converts an IEnumerable of type Tab into a hierarchical collection of TabNodes.
// If Tab has no parent its ParentId is -1
public class Tab
{
public int TabId { get; set; }
public string TabName { get; set; }
public int Level { get; set; }
public int ParentId { get; set; }
}
public class TabNode
{
public TabInfo Tab { get; set; }
public IEnumerable<TabNode> ChildNodes { get; set; }
public int Depth { get; set; }
}
For instance, the following would give you a collection of TabNodes who are below a Parent with TabId 32 - the maximum level of depth is 4.
IEnumerable<Tab> tabs = GetTabs();
IEnumerable<TabNode> = tabs.AsNavigationHierarchy(32,4);
This is confusing and not very friendly for further refinement. What If I'd like to specify a certain Level instead of a ParentID?
What I'd like to do is something like this:
IEnumerable<TabNode> = tabs.AsNavigationHierarchy().WithStartLevel(2).WithMaxDepth(5)
I'm stuck as how to do this elegantly. Can you help me?
This is my current function which is called by my extension methods (based on an article I've found on www.scip.be).
private static IEnumerable<TabNode>
CreateHierarchy(
IEnumerable<TabInfo> tabs,
int startTabId,
int maxDepth,
int depth)
{
IEnumerable<TabInfo> children;
children = tabs.Where(i => i.ParentId.Equals(startTabId));
if (children.Count() > 0)
{
depth++;
if ((depth <= maxDepth) || (maxDepth == 0))
{
foreach (var childTab in children)
yield return
new TabNode()
{
Tab = childTab,
ChildNodes =
CreateHierarchy(tabs, childTab.TabID, maxDepth, depth),
Depth = depth
};
}
}
}
tabs.AsNavigationHeirachy could return a HerirchyQuery object which your next extension methods would then expect. This will let you chain them together.