Composite with custom comparer - c#

I have problem with implementing custom comparer in Composite C#. I would like to pass custom comparer my Composite object.
Here is IComponent interface:
namespace composite
{
public interface IComponent<T>
{
void Add(IComponent<T> component);
IComponent<T> Find(T item);
IComponent<T> Remove(T item);
string Display(int depth);
string Name { get; set; }
}
}
For Component a Composite object I´m using the same interface.
Composite (my Collection of Components)
namespace composite
{
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
public class Composite<T> : IComponent<T>
{
private ICollection<IComponent<T>> components;
private IComponent<T> holder;
public Composite(string name)
{
this.Name = name;
this.holder = null;
this.components = new List<IComponent<T>>();
}
public string Name { get; set; }
public void Add(IComponent<T> component)
{
this.components.Add(component);
}
public IComponent<T> Find(T item)
{
this.holder = this;
if (item.Equals(this.Name))
{
return this;
}
IComponent<T> found = null;
//this.components.Select(s => s.Name == item)
foreach (IComponent<T> component in this.components)
{
found = component.Find(item);
if (found != null)
{
break;
}
}
return found;
}
public IComponent<T> Remove(T item)
{
this.holder = this;
IComponent<T> p = holder.Find(item);
if (this.holder != null)
{
(this.holder as Composite<T>).components.Remove(p);
return this.holder;
}
else
{
return this;
}
}
//public IEnumerable<IComponent<T>> Dsiplay(int depth)
public string Display(int depth)
{
StringBuilder s = new StringBuilder();
s.Append("set " + this.Name + " length :" + this.components.Count + "\n");
foreach (IComponent<T> component in components)
{
s.Append(component.Display(depth + 2));
}
return s.ToString();
}
}
}
Component:
namespace composite
{
using System;
using System.Collections.Generic;
public class Component<T> : IComponent<T>
{
public Component(string name)
{
this.Name = name;
}
public string Display(int depth)
{
return new string('-', depth) + this.Name + "\n";
}
public string Name { get; set; }
public IComparer<T> MyComparer { get; private set; }
//public IComparer<T> myComparer { set; }
public void Add(IComponent<T> item)
{
throw new NotImplementedException();
}
public IComponent<T> Find(T item)
{
//Here I want to use comparer object, instead of Equals
//Something like this:
//return MyComparer.Compare( ... ) == 0 ? this : null;
return item.Equals(this.Name) ? this : null;
}
public IComponent<T> Remove(T item)
{
throw new NotImplementedException();
}
}
}
And finally my main function:
namespace composite
{
class Program
{
static void Main(string[] args)
{
//Creating custom comparer and pass to the constructor composite?
IComparer<string> myComparer = new ...
IComponent<string> comp1 = new Component<string>("Komponenta 1");
IComponent<string> comp2 = new Component<string>("Komponenta 2");
IComponent<string> comp3 = new Component<string>("Komponenta 3");
IComponent<string> comp4 = new Component<string>("Komponenta 4");
IComponent<string> comp5 = new Component<string>("Komponenta 5");
IComponent<string> composite = new Composite<string>("Composite 1");
IComponent<string> composite2 = new Composite<string>("Composite 2");
composite.Add(comp1);
composite.Add(comp2);
composite.Add(comp3);
composite.Add(comp4);
composite.Add(comp5);
composite2.Add(comp1);
composite2.Add(comp2);
composite2.Add(comp3);
composite2.Add(comp4);
composite2.Add(comp5);
composite.Add(composite2);
Console.Write(composite.Display(0));
}
}
}
Can you help me with implement custom comparer and pass to Find method?
Is it good way or not?
Very thanks

The main issue here is that:
the components have a Name property
the components don't use the generic type parameter T
the Find method gets a generic item of type T to find and compares
it with the component name - this will only work for IComponent<string>
So the most simple (and my preferred) solution would be to get rid of the comparer.
Instead, I would a define find method with a predicate (as mentioned in the comments).
But you asked for a comparer!
So there are a few adjustments to make:
define a property T Data {get; set;} in IComponent<T> and
provide an implementation in Component<T> and Composite<T>
switch from IComparer<T> to IEqualityComparer<IComponent<T>> if you want to
because you want to search equal components and not compare elements
change the remove method accordingly
In code (shortened a little):
public interface IComponent<T>
{
void Add(IComponent<T> component);
IComponent<T> Find(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer);
IComponent<T> Find(Predicate<IComponent<T>> condition)
bool Remove(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer);
string Display(int depth);
string Name { get; set; }
T Data { get; set; }
}
public class Component<T> : IComponent<T>
{
public T Data { get; set; }
public string Name { get; set; }
public Component(string name)
=> Name = name;
public string Display(int depth) =>
new string('-', depth) + Name + "\n";
public IComponent<T> Find(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
=> comparer.Equals(item, this) ? this : null;
public IComponent<T> Find(Predicate<IComponent<T>> condition)
=> condition(this) ? this : null;
public void Add(IComponent<T> item)
=> throw new InvalidOperationException();
public bool Remove(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
=> throw new InvalidOperationException();
}
public class Composite<T> : IComponent<T>
{
private IList<IComponent<T>> components = new List<IComponent<T>>();
public T Data { get; set; }
public string Name { get; set; }
public Composite(string name)
=> Name = name;
public void Add(IComponent<T> component)
=> components.Add(component);
public IComponent<T> Find(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
{
if (comparer.Equals(item, this))
return this;
else
foreach (var component in components)
{
var childItem = component.Find(item, comparer);
if (childItem != null)
return childItem;
}
return null;
}
public bool Remove(IComponent<T> item, IEqualityComparer<IComponent<T>> comparer)
{
var result = false;
for (var i = components.Count - 1; i >= 0; i--)
if (comparer.Equals(components[i], item))
{
components.RemoveAt(i);
result = true;
}
return result;
}
public IComponent<T> Find(Predicate<IComponent<T>> condition)
{
if (condition(this))
return this;
foreach (var item in components)
{
var result = item.Find(condition);
if (result != null)
return result;
}
return null;
}
public string Display(int depth)
{
var s = new StringBuilder();
s.Append(new string('-', depth) + "set " + Name + " length :" + components.Count + "\n");
foreach (var component in components)
s.Append(component.Display(depth + 2));
return s.ToString();
}
}
Two comparer implementations would be:
public class DefaultComparer<T> : IEqualityComparer<IComponent<T>>
{
public bool Equals(IComponent<T> x, IComponent<T> y)
=> EqualityComparer<T>.Default.Equals(x.Data, y.Data);
public int GetHashCode(IComponent<T> obj)
=> EqualityComparer<T>.Default.GetHashCode(obj.Data);
}
public class NameComparer<T> : IEqualityComparer<IComponent<T>>
{
public bool Equals(IComponent<T> x, IComponent<T> y)
=> string.Equals(x.Name, y.Name);
public int GetHashCode(IComponent<T> obj)
=> (obj.Name ?? string.Empty).GetHashCode();
}
How to use that?
If you want to search for a component with a given name, you can now use:
var element1 = composite.Find(new Component<string>("Komponenta 5"), new NameComparer<string>());
Console.WriteLine(element1.Name);
Or event simpler:
var element2 = composite.Find(t => string.Equals(t.Name, "Komponenta 5"));
Console.WriteLine(element2.Name);

Related

How to implement the IEnumerator<T> interface for the composite pattern?

I have a collection of objects following the composite pattern. They form a tree structure that I want to traverse with an IEnumerator. I'm translating some java code from the book "Head First Design Patterns". I've implemented two classes that implement the IEnumerator interface: CompositeIterator and NullIterator.
This is the java code I want to translate to C#.
Also, this is my wanted implementation of the Waitress class, when I only have to call MoveNext() and Current to traverse the whole tree structure.
Now my code is not entering the while loop that involves the iterator, and I want to print the vegetarian MenuItem objects on the console.
Here is my code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Iterator
{
class Program
{
static void Main(string[] args)
{
MenuComponent pancakeHouseMenu = new Menu("PANCAKEHOUSE MENU", "Breakfast");
MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch");
MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course!");
MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
allMenus.Add(pancakeHouseMenu);
allMenus.Add(dinerMenu);
allMenus.Add(cafeMenu);
pancakeHouseMenu.Add(new MenuItem("K&B Pancake breakfast", "pancakes with scrambled eggs, and toast", true, 2.99));
pancakeHouseMenu.Add(new MenuItem("Regular Pancake breakfast", "pancakes with fried eggs, sausage", false, 2.99));
dinerMenu.Add(new MenuItem("Veggie burguer and air fries", "Veggie burguer on a whole wheat bun, lettuce, tomato and fries", true, 3.99));
dinerMenu.Add(new MenuItem("Soup of the day", "Soup of the day with a side salad", false, 3.69));
dinerMenu.Add(dessertMenu);
dessertMenu.Add(new MenuItem("Apple pie", "Apple pie with a flakey crust, topped with vanilla ice cream", true, 1.59));
cafeMenu.Add(new MenuItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99));
cafeMenu.Add(new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99));
Waitress waitress = new Waitress(allMenus);
waitress.PrintVegetarianMenu();
}
}
class Waitress
{
private MenuComponent AllMenus { get; set; }
public Waitress(MenuComponent allMenus)
{
AllMenus = allMenus;
}
public void PrintMenu()
{
AllMenus.Print();
}
public void PrintVegetarianMenu()
{
CompositeIterator<MenuComponent> iterator = (CompositeIterator<MenuComponent>)AllMenus.CreateIterator();
Console.WriteLine("VEGATARIAN MENU");
// this loop is never entered
while (iterator.MoveNext())
{
Console.WriteLine("inside while loop");
MenuComponent menuComponent = (MenuComponent)iterator.Current;
Console.WriteLine(menuComponent.Name);
try
{
if (menuComponent.Vegetarian)
{
menuComponent.Print();
}
}
catch (NotSupportedException e)
{
Console.WriteLine("Operation not supported.");
}
}
}
}
/*
Methods of MenuComponent class are virtual, because we sometimes want to use the default behavior. The CreateIterator method is abstract.
*/
abstract class MenuComponent
{
// Composite methods
public virtual void Add(MenuComponent menuComponent)
{
throw new NotSupportedException();
}
public virtual void Remove(MenuComponent menuComponent)
{
throw new NotSupportedException();
}
public virtual MenuComponent GetChild(int i)
{
throw new NotSupportedException();
}
// End of composite methods
// Operation methods
public virtual string Name
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
public virtual string Description
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
public virtual bool Vegetarian
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
public virtual double Price
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
public virtual void Print()
{
throw new NotSupportedException();
}
// End of operation methods
public abstract IEnumerator CreateIterator();
}
public sealed class CompositeIterator<T> : IEnumerator<T> {
private readonly Stack<IEnumerator<T>> Stack = new Stack<IEnumerator<T>>();
public CompositeIterator(IEnumerator<T> initial)
{
Stack.Push(initial);
}
public bool MoveNext()
{
while (Stack.Any())
{
if (!Stack.Peek().MoveNext())
{
Stack.Pop().Dispose();
continue;
}
var tmp = Current as IEnumerable<T>;
if (tmp != null) { Stack.Push(tmp.GetEnumerator()); }
}
return false;
}
public void Reset() { throw new NotSupportedException(); }
public T Current => Stack.Peek() != null ? Stack.Peek().Current : default(T);
object IEnumerator.Current => Current;
public void Dispose()
{
if (!Stack.Any()) { return; }
try {
foreach (var x in Stack) {
x.Dispose();
}
} catch { }
}
}
public sealed class NullIterator<T> : IEnumerator<T> {
public NullIterator() {}
public bool MoveNext()
{
return false;
}
public void Reset() { throw new NotSupportedException(); }
public T Current
{
get
{
return default(T);
}
}
object IEnumerator.Current => Current;
public void Dispose()
{
return;
}
}
// This is a tree leaf
class MenuItem : MenuComponent
{
public override string Name { get; set; }
public override string Description { get; set; }
public override bool Vegetarian { get; set; }
public override double Price { get; set; }
public MenuItem(string name, string description, bool vegetarian, double price)
{
Name = name;
Description = description;
Vegetarian = vegetarian;
Price = price;
}
public override void Print()
{
Console.Write(" " + Name);
if (Vegetarian)
{
Console.Write("(v)");
}
Console.Write(", " + Price);
Console.Write(" -- " + Description);
}
public override IEnumerator CreateIterator()
{
return new NullIterator<MenuItem>();
}
}
// This is a tree node
class Menu : MenuComponent
{
public List<MenuComponent> MenuComponents;
public override string Name { get; set; }
public override string Description { get; set; }
public Menu(string name, string description)
{
Name = name;
Description = description;
MenuComponents = new List<MenuComponent>();
}
public override void Add(MenuComponent menuComponent)
{
MenuComponents.Add(menuComponent);
}
public override void Remove(MenuComponent menuComponent)
{
MenuComponents.Remove(menuComponent);
}
public override MenuComponent GetChild(int i)
{
return MenuComponents[i];
}
// we have to use recursion to print all the hierarchy
public override void Print()
{
Console.Write("\n" + Name);
Console.WriteLine(", " + Description);
Console.WriteLine("--------------");
IEnumerator iterator = MenuComponents.GetEnumerator();
while(iterator.MoveNext())
{
MenuComponent menuComponent = (MenuComponent)iterator.Current;
menuComponent.Print();
Console.Write("\n");
}
}
public override IEnumerator CreateIterator()
{
return new CompositeIterator<MenuComponent>(MenuComponents.GetEnumerator());
}
}
}
This is the closest I can come to getting what you want without creating my own iterators. I don't feel there is a need to re-code what already exists in the dotnet framework.
class Program
{
static void Main(string[] args)
{
//var all = new Menu("ALL SECTIONS", "All menu sections");
var pancakeHouseMenu = new Menu("PANCAKEHOUSE MENU", "Breakfast",
new MenuSection("PANCAKES", "Breakfast Pancakes Selection",
new MenuItem("K&B Pancake breakfast", "pancakes with scrambled eggs, and toast", true, 2.99m),
new MenuItem("Regular Pancake breakfast", "pancakes with fried eggs, sausage", false, 2.99m)));
var dinnerMenu = new Menu("DINNER MENU", "Lunch",
new MenuSection("","",
new MenuItem("Veggie burger and air fries", "Veggie burguer on a whole wheat bun, lettuce, tomato and fries", true, 3.99m),
new MenuItem("Soup of the day", "Soup of the day with a side salad", false, 3.69m)));
var cafeMenu = new Menu("CAFE MENU", "Dinner",
new MenuSection("", "",
new MenuItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99m),
new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99m)),
new MenuSection("DESSERT MENU", "Dessert of course!",
new MenuItem("Apple pie", "Apple pie with a flakey crust, topped with vanilla ice cream", true, 1.59m)));
var waiter = new Waitress(pancakeHouseMenu, dinnerMenu, cafeMenu);
//waiter.Print();
//waiter.PrintVegenerian();
WriteFileAndOpenNotepad(waiter.ToString());
WriteFileAndOpenNotepad(waiter.ToVegetarianString());
}
static void WriteFileAndOpenNotepad(string text)
{
var fn = Path.GetTempFileName();
fn=Path.ChangeExtension(fn, ".txt");
File.WriteAllText(fn, text);
Process.Start(fn);
}
}
public abstract class MenuComponent
{
public string Description { get; private set; }
public string Name { get; private set; }
protected MenuComponent(string name, string description)
{
this.Name=name;
this.Description=description;
}
public abstract override string ToString();
public void Print()
{
Console.WriteLine(ToString());
}
}
public class MenuItem : MenuComponent
{
public MenuItem(string name, string description, bool vegeterian, decimal price)
: base(name, description)
{
this.Price=price;
this.Vegetarian=vegeterian;
}
public decimal Price { get; set; }
public bool Vegetarian { get; set; }
public override string ToString()
{
//Use 28 columns for the item name
return string.Format("{0,28}{1}, {2} -- {3}",
Name, Vegetarian ? "(v)" : " ", Price, Description);
}
}
public class MenuSection : MenuComponent
{
public MenuSection(string name, string description, params MenuItem[] items)
: this(name, description, items as IEnumerable<MenuItem>)
{ }
public MenuSection(string name, string description, IEnumerable<MenuItem> items) : base(name, description)
{
this.Items=new List<MenuItem>(items);
}
public List<MenuItem> Items { get; private set; }
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine();
if(Name.Length>0)
{
sb.AppendFormat("{0}, {1}", Name, Description);
sb.AppendLine();
sb.AppendLine("--------------");
}
foreach(var item in Items)
{
sb.AppendLine();
sb.AppendLine(item.ToString());
}
return sb.ToString();
}
public MenuSection VegeterianSections
{
get
{
var veg = Items.Where((item) => item.Vegetarian);
return new MenuSection(Name, Description, veg);
}
}
}
public class Menu : MenuComponent
{
public Menu(string name, string description, IEnumerable<MenuSection> sections)
: base(name, description)
{
this.MenuSections=new List<MenuSection>(sections);
}
public Menu(string name, string description, params MenuSection[] sections)
: this(name, description, sections as IEnumerable<MenuSection>)
{ }
public List<MenuSection> MenuSections { get; private set; }
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendFormat("[{0}, {1}]", Name, Description);
sb.AppendLine();
sb.AppendLine("==============");
foreach(var section in MenuSections)
{
sb.AppendLine();
sb.AppendLine(section.ToString());
}
return sb.ToString();
}
public Menu VegeraterianMenu
{
get
{
return new Menu(Name, Description, MenuSections.Select((section)=> section.VegeterianSections));
}
}
}
public class Waitress
{
public Waitress(params Menu[] all)
{
this.AllMenus=new List<Menu>(all);
this.VegeratianMenu=all.Select((menu)=>menu.VegeraterianMenu).ToList();
}
public IList<Menu> AllMenus { get; private set; }
public IList<Menu> VegeratianMenu { get; private set; }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("*** A L L I T E M S ***");
foreach(var item in AllMenus)
{
sb.AppendLine("************************");
sb.AppendLine(item.ToString());
}
return sb.ToString();
}
public string ToVegetarianString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("*** V E G I T E M S ***");
foreach(var item in VegeratianMenu)
{
sb.AppendLine("************************");
sb.AppendLine(item.ToString());
}
return sb.ToString();
}
public void Print()
{
Console.WriteLine(ToString());
}
public void PrintVegenerian()
{
Console.WriteLine(ToVegetarianString());
}
}
After contacting people on the C# freenode IRC channel, one of them gave me a solution better than the one in the book and the answer I got here. This is the code, and with this answer I hope is clear what I wanted:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Iterator
{
class Program
{
static void Main(string[] args)
{
MenuComponent pancakeHouseMenu = new Menu("PANCAKEHOUSE MENU", "Breakfast");
MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch");
MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course!");
MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");
allMenus.Add(pancakeHouseMenu);
allMenus.Add(dinerMenu);
allMenus.Add(cafeMenu);
pancakeHouseMenu.Add(new MenuItem("K&B Pancake breakfast", "pancakes with scrambled eggs, and toast", true, 2.99));
pancakeHouseMenu.Add(new MenuItem("Regular Pancake breakfast", "pancakes with fried eggs, sausage", false, 2.99));
dinerMenu.Add(new MenuItem("Veggie burguer and air fries", "Veggie burguer on a whole wheat bun, lettuce, tomato and fries", true, 3.99));
dinerMenu.Add(new MenuItem("Soup of the day", "Soup of the day with a side salad", false, 3.69));
dinerMenu.Add(dessertMenu);
dessertMenu.Add(new MenuItem("Apple pie", "Apple pie with a flakey crust, topped with vanilla ice cream", true, 1.59));
cafeMenu.Add(new MenuItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99));
cafeMenu.Add(new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99));
cafeMenu.Add(dessertMenu);
Waitress waitress = new Waitress(allMenus);
waitress.PrintVegetarianMenu();
}
}
class Waitress
{
private MenuComponent AllMenus { get; set; }
public Waitress(MenuComponent allMenus)
{
AllMenus = allMenus;
}
public void PrintMenu()
{
AllMenus.Print();
}
public void PrintVegetarianMenu()
{
Console.WriteLine("VEGATARIAN MENU");
foreach (MenuComponent menuComponent in AllMenus)
{
try
{
if (menuComponent.Vegetarian)
{
menuComponent.Print();
Console.Write("\n");
}
}
catch (NotSupportedException)
{
Console.WriteLine("Operation not supported.");
}
}
}
}
abstract class MenuComponent : IEnumerable<MenuComponent>
{
// Composite methods
public abstract void Add(MenuComponent menuComponent);
public abstract void Remove(MenuComponent menuComponent);
public abstract MenuComponent GetChild(int i);
// End of composite methods
// Operation methods
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual bool Vegetarian { get; set; }
public virtual double Price { get; set; }
public abstract void Print();
public abstract IEnumerator<MenuComponent> GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
/// This is a tree leaf
class MenuItem : MenuComponent
{
public MenuItem(string name, string description, bool vegetarian, double price)
{
Name = name;
Description = description;
Vegetarian = vegetarian;
Price = price;
}
public override void Print()
{
Console.Write(" " + Name);
if (Vegetarian)
{
Console.Write("(v)");
}
Console.Write(", " + Price);
Console.Write(" -- " + Description);
}
public override IEnumerator<MenuComponent> GetEnumerator()
{
yield break;
}
public override void Add(MenuComponent menuComponent)
{
throw new NotSupportedException();
}
public override void Remove(MenuComponent menuComponent)
{
throw new NotSupportedException();
}
public override MenuComponent GetChild(int i)
{
throw new NotSupportedException();
}
}
/// This is a tree node
class Menu : MenuComponent
{
private List<MenuComponent> MenuComponents;
public Menu(string name, string description)
{
Name = name;
Description = description;
MenuComponents = new List<MenuComponent>();
}
public override void Add(MenuComponent menuComponent)
{
MenuComponents.Add(menuComponent);
}
public override void Remove(MenuComponent menuComponent)
{
MenuComponents.Remove(menuComponent);
}
public override MenuComponent GetChild(int i)
{
return MenuComponents[i];
}
// we have to use recursion to print all the hierarchy
public override void Print()
{
Console.Write("\n" + Name);
Console.WriteLine(", " + Description);
Console.WriteLine("--------------");
foreach (MenuComponent menuComponent in MenuComponents)
{
menuComponent.Print();
Console.Write("\n");
}
}
public override IEnumerator<MenuComponent> GetEnumerator()
{
var components = new Stack<MenuComponent>(new[] { this });
while (components.Any())
{
MenuComponent component = components.Pop();
yield return component;
var menu = component as Menu;
if (menu != null)
{
foreach (var n in menu.MenuComponents) components.Push(n);
}
}
}
}
}

Should I unit test by using database simulations

I'm currently in the process of writing various unit tests for an application.
Now, I do have tests to check if the code is working, which it is. But should I also simulate, for example:
Database Unavailable.
Database query returns null.
Database query takes very long time to execute.
I know this is not a coding question, but I would like to have some general thoughts about it.
If it's needed, I thought of the following approach:
SettingsUnavailableMock.Setup(x => x.PageRepository.All()).Throws(new Exception());
SettingsUnavailableMock.Setup(x => x.PageRepository.Get(It.IsAny<int>())).Throws(new Exception());
SettingsUnavailableMock.Setup(x => x.PageRepository.Get(It.IsAny<string>())).Throws(new Exception());
SettingsUnavailableMock.Setup(x => x.PageRepository.Refresh(It.IsAny<Page>())).Throws(new Exception());
SettingsUnavailableMock.Setup(x => x.PageRepository.Save()).Throws(new Exception());
Off course, add for all the repositories.
Then in my test class, I can just choose which Mock I would like to use.
Ideally you should test all of the above; however, it depends on your circumstances. Personally I always test everything that I can feasibly test.
Queries that take a long time are very realistic.
Databases being unavialable is also very realistic.
The Query returning null I'm not so sure about; however, if that is a realistic scenario then by all means stub it and test it.
Update - based on comments I thought this would be a good thing to add
public interface IRepository<T> where T : IRepositoryEntry, new()
{
event EventHandler<RepositoryOperationEventArgs> InsertEvent;
event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
IList<String> PrimaryKeys { get; }
void Insert(T Entry);
void Update(T Entry);
void Delete(Predicate<T> predicate);
bool Exists(Predicate<T> predicate);
T Retrieve(Predicate<T> predicate);
IEnumerable<T> RetrieveAll();
}
public interface IRepositoryEntry
{
IList<String> GetPrimaryKeys();
}
public class OracleRepository
{
const string ConnectionString = "*"
public static IDbConnection GetIDbConnection()
{
IDbConnection connection = new OracleConnection(ConnectionString).OpenConnection();
return connection;
}
public IDbConnection GetConnection()
{
IDbConnection connection = new OracleConnection(ConnectionString).OpenConnection();
return connection;
}
}
public class OracleRepository<T> : OracleRepository, IDisposable, IRepository<T> where T : RepositoryEntryBase, IRepositoryEntry, new()
{
/// <summary>
/// Gets all property names from a type.
/// </summary>
/// <returns>IEnumerable of strings</returns>
static IEnumerable<String> GetEntryPropertyNames(Type type)
{
foreach (var propInfo in type.GetProperties())
yield return propInfo.Name;
}
public event EventHandler<RepositoryOperationEventArgs> InsertEvent;
public event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
public event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
#region Properties
public IList<String> PrimaryKeys
{
get
{
return primaryKeys.AsReadOnly();
}
private set
{
primaryKeys = new List<String>(value);
}
}
public IList<String> Properties { get; private set; }
public String InsertText { get; private set; }
public String UpdateText { get; private set; }
public String DeleteText { get; private set; }
public String SelectText { get; private set; }
#endregion
#region Fields
IDbConnection connection;
IDbTransaction transaction;
List<String> primaryKeys;
#endregion
#region Constructors
public OracleRepository()
{
PrimaryKeys = new List<String>(new T().GetPrimaryKeys());
Properties = new List<String>(GetEntryPropertyNames(typeof(T))).AsReadOnly();
InsertText = GenerateInsertText();
UpdateText = GenerateUpdateText();
SelectText = GenerateSelectText();
DeleteText = GenerateDeleteText();
connection = GetConnection();
}
#endregion
#region Interface Implementations
public void Insert(T Entry)
{
Insert(connection, Entry);
}
public void Update(T Entry)
{
Update(connection, Entry);
}
public void Delete(Predicate<T> predicate)
{
Delete(connection, predicate);
}
public T Retrieve(Predicate<T> predicate)
{
return Retrieve(connection, predicate);
}
public bool Exists( Predicate<T> predicate)
{
return Exists(connection, predicate);
}
public IEnumerable<T> RetrieveAll()
{
return RetrieveAll(connection);
}
public void Dispose()
{
if (transaction != null)
transaction.Dispose();
connection.Dispose();
}
#endregion
#region Public Methods
public void StartTransaction()
{
if (transaction != null)
throw new InvalidOperationException("Transaction is already set. Please Rollback or commit transaction");
transaction = connection.BeginTransaction();
}
public void CommitTransaction()
{
transaction.Commit();
transaction.Dispose();
transaction = null;
}
public void RollbackTransaction()
{
transaction.Rollback();
transaction.Dispose();
transaction = null;
}
public void Insert(IDbConnection connection, T Entry)
{
Type type = typeof(T);
List<Object> args = new List<Object>();
for (int i = 0; i < Properties.Count; i++)
args.Add(type.GetProperty(Properties[i]).GetValue(Entry));
connection.NonQuery(InsertText, args.ToArray());
if (InsertEvent != null)
InsertEvent(this, new OracleRepositoryOperationEventArgs() { Entry = Entry, Transaction = (transaction != null) });
}
public void Update(IDbConnection connection, T Entry)
{
Type type = typeof(T);
List<Object> args = new List<Object>();
foreach (var propertyName in Properties.Where(p => !PrimaryKeys.Any(k => k == p)))
args.Add(type.GetProperty(propertyName).GetValue(Entry));
foreach (var PropertyName in PrimaryKeys)
args.Add(type.GetProperty(PropertyName).GetValue(Entry));
connection.NonQuery(UpdateText, args.ToArray());
if (UpdateEvent != null)
UpdateEvent(this, new OracleRepositoryOperationEventArgs() { Entry = Entry, Transaction = (transaction != null) });
}
public void Delete(IDbConnection connection, Predicate<T> predicate)
{
var entryList = RetrieveAll(connection).Where(new Func<T, bool>(predicate));
Type type = typeof(T);
foreach(var entry in entryList)
{
List<Object> args = new List<Object>();
foreach (var PropertyName in PrimaryKeys)
args.Add(type.GetProperty(PropertyName).GetValue(entry));
connection.NonQuery(DeleteText, args.ToArray());
if (DeleteEvent != null)
DeleteEvent(this, new OracleRepositoryOperationEventArgs() { Entry = null, Transaction = (transaction != null) });
}
}
public T Retrieve(IDbConnection connection, Predicate<T> predicate)
{
return RetrieveAll(connection).FirstOrDefault(new Func<T, bool>(predicate));
}
public bool Exists(IDbConnection connection, Predicate<T> predicate)
{
return RetrieveAll(connection).Any(new Func<T, bool>(predicate));
}
public IEnumerable<T> RetrieveAll(IDbConnection connection)
{
List<T> collection = new List<T>();
var result = connection.Query(SelectText);
foreach (var row in result.Tuples)
collection.Add(RepositoryEntryBase.FromPlexQueryResultTuple(new T(), row) as T);
return collection;
}
#endregion
#region Private Methods
String GenerateInsertText()
{
String statement = "INSERT INTO {0}({1}) VALUES ({2})";
//Do first entry here becasse its unique input.
String columnNames = Properties.First();
String delimiter = ", ";
String bph = ":a";
String placeHolders = bph + 0;
//Start # 1 since first entry is already done
for (int i = 1; i < Properties.Count; i++)
{
columnNames += delimiter + Properties[i];
placeHolders += delimiter + bph + i;
}
statement = String.Format(statement, typeof(T).Name, columnNames, placeHolders);
return statement;
}
String GenerateUpdateText()
{
String bph = ":a";
String cvpTemplate = "{0} = {1}";
String statement = "UPDATE {0} SET {1} WHERE {2}";
//Can only set Cols that are not a primary Keys, Get those Columns
var Settables = Properties.Where(p => !PrimaryKeys.Any(k => k == p)).ToList();
String cvp = String.Format(cvpTemplate, Settables.First() , bph + 0 );
String condition = String.Format(cvpTemplate, PrimaryKeys.First(), bph + Settables.Count);
//These are the values to be set | Start # 1 since first entry is done above.
for (int i = 1; i < Settables.Count; i++)
cvp += ", " + String.Format(cvpTemplate, Settables[i], bph + i);
//This creates the conditions under which the values are set. | Start # 1 since first entry is done above.
for (int i = Settables.Count + 1; i < Properties.Count; i++)
condition += ", " + String.Format(cvpTemplate, PrimaryKeys[i - Settables.Count], bph + i);
statement = String.Format(statement, typeof(T).Name, cvp, condition);
return statement;
}
String GenerateDeleteText()
{
String bph = ":a";
String cvpTemplate = "{0} = {1}";
String statement = "DELETE FROM {0} WHERE {1}";
String condition = String.Format(cvpTemplate, PrimaryKeys.First(), bph + 0);
for (int i =1; i < PrimaryKeys.Count; i++)
condition += ", " + String.Format(cvpTemplate, PrimaryKeys[i], bph + i);
statement = String.Format(statement, typeof(T).Name, condition);
return statement;
}
String GenerateSelectText()
{
String statement = "SELECT * FROM {0}";
statement = String.Format(statement, typeof(T).Name);
return statement;
}
#endregion
}
This is what an element of that implements IReposistoryEntry looks like:
public class APPS : RepositoryEntryBase, IRepositoryEntry
{
public int APP_ID { get; set; }
public string AUTH_KEY { get; set; }
public string TITLE { get; set; }
public string DESCRIPTION { get; set; }
public int IS_CLIENT_CUSTOM_APP { get; set; }
public APPS() : base() {
primaryKeys.Add("APP_ID");
}
public APPS(PlexQueryResultTuple plexTuple) : base(plexTuple) { }
}
public class RepositoryEntryBase
{
public static RepositoryEntryBase FromPlexQueryResultTuple( RepositoryEntryBase reb, PlexQueryResultTuple plexTuple)
{
if (plexTuple.parent == null)
throw new NotSupportedException("This Operation is Not supported by this PlexTuple.");
Type type = reb.GetType();
var pInfo = type.GetProperties();
PlexQueryResult result = plexTuple.parent;
foreach (var p in pInfo)
{
int index = result.Tuples.IndexOf(plexTuple);
if (result[p.Name, index] == null)
continue;
var conversationType = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType;
object value = Convert.ChangeType(result[p.Name, index], (result[p.Name, index] != null)?conversationType: p.PropertyType);
p.SetValue(reb, value);
}
return reb;
}
protected IList<String> primaryKeys;
public RepositoryEntryBase()
{
primaryKeys = new List<String>();
}
public RepositoryEntryBase(PlexQueryResultTuple plexTuple) : this()
{
FromPlexQueryResultTuple(this, plexTuple);
}
public IList<String> GetPrimaryKeys()
{
return primaryKeys;
}
}
Below I've posted the mock database. What is important to recogonize here is that the tests actually use the interface and I can exchange can interchange the real database with the mock one very easily. I like to reuse this code a fair amount (its actually in a dll for me). So I don't have to recode database code for every project.
public class InMemoryRepository<T> : IRepository<T> where T : IRepositoryEntry, new()
{
//RepositoryEntryBase,
public event EventHandler<RepositoryOperationEventArgs> InsertEvent;
public event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
public event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
public IList<String> PrimaryKeys { get; protected set; }
List<T> data;
public InMemoryRepository() {
PrimaryKeys = new List<String>(new T().GetPrimaryKeys());
data = new List<T>();
}
public void Insert(T Entry){
if(Get(Entry) != null)
throw new Exception("Duplicate Entry - Identical Key already exists");
data.Add(Entry);
if (InsertEvent != null)
InsertEvent(this, new RepositoryOperationEventArgs() { Entry = Entry });
}
public void Update(T Entry){
var obj = Get(Entry);
if (obj == null)
throw new Exception("Object does not exist");
obj = Entry;
if (UpdateEvent != null)
UpdateEvent(this, new RepositoryOperationEventArgs() { Entry = obj });
}
public void Delete(Predicate<T> predicate)
{
data.RemoveAll(predicate);
if (DeleteEvent != null)
DeleteEvent(this, new RepositoryOperationEventArgs() { Entry = null });
}
public bool Exists(Predicate<T> predicate)
{
return data.Exists(predicate);
}
public T Retrieve(Predicate<T> predicate)
{
return data.FirstOrDefault(new Func<T, bool>(predicate));
}
public IEnumerable<T> RetrieveAll()
{
return data.ToArray();
}
T Get(T Entry)
{
//Returns Entry based on Identical PrimaryKeys
Type entryType = typeof(T);
var KeyPropertyInfo = entryType.GetProperties().Where(p => PrimaryKeys.Any(p2 => p2 == p.Name));
foreach (var v in data)
{
//Assume the objects are identical by default to prevent false positives.
Boolean AlreadyExists = true;
foreach (var property in KeyPropertyInfo)
if (!property.GetValue(v).Equals(property.GetValue(Entry)))
AlreadyExists = false;
if (AlreadyExists)
return v;
}
return default(T);
}
}
It really depends on what your code interacting with the database is doing.
The philosophy behind unit test is to test a class by itself. All its external dependencies should be mocked.
However you might also check if your class correctly uses its dependencies. This would be an interaction test
And eventually, if everything sounds good you want to check if the whole system works together. It is an integration test.
Note that there is some librairies allowing you to perform integration tests more easily, such as Specflow
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
public interface IProductRepository
{
List<Product> LoadProducts();
}
public class ProductRepository : IProductRepository
{
public List<Product> LoadProducts()
{
// database code which returns the list of product
return new List<Product>();
}
}
public class StorageStatisticsGenerator
{
private readonly IProductRepository _repository;
public StorageStatisticsGenerator(IProductRepository repository)
{
_repository = repository;
}
public int ComputeNumberOfProducts()
{
var products = _repository.LoadProducts();
return products.Count;
}
}
Given the following class you might want to test different kind of things.
[TestFixture]
public class StorageStatisticsGeneratorTests
{
private Mock<IProductRepository> _productRepository;
private StorageStatisticsGenerator _statisticGenerator;
[SetUp]
public void Setup()
{
_productRepository = new Mock<IProductRepository>();
_statisticGenerator = new StorageStatisticsGenerator(_productRepository.Object);
}
// In this test we test if the statistic generator works correctly
// This is a UNIT TEST
[Test]
public void ComputeNumberOfProducts_Should_Returns_TheCorrectCount()
{
// Arrange
_productRepository.Setup(p => p.LoadProducts()).Returns(new List<Product>
{
new Product(), new Product(), new Product()
});
// Act
int result = _statisticGenerator.ComputeNumberOfProducts();
// Assert
Assert.AreEqual(3, result);
}
// In this test we test if the statistic generator use the repository as expected
// This is an INTERACTION TEST, you could check corner case using "real life data"
[Test]
public void ComputeNumberOfProducts_Should_Use_The_Product_Repository()
{
// Arrange
_productRepository.Setup(p => p.LoadProducts()).Returns(new List<Product>
{
new Product()
});
// Act
_statisticGenerator.ComputeNumberOfProducts();
// Assert
_productRepository.Verify(p => p.LoadProducts());
}
// In this test we use the real repository this is an INTEGRATION TEST
// You can flag this kind of slow test to run only during the night for instabce
[Test, Category("Nightly")]
public void ComputeNumberOfProducts_Should_Correctly_Integrate_With_ProductRepository()
{
// Arrange
_statisticGenerator = new StorageStatisticsGenerator(new ProductRepository());
// Act
_statisticGenerator.ComputeNumberOfProducts();
// Assert
_productRepository.Verify(p => p.LoadProducts());
}
}
If you want to know more, you can read The art of unit testing

.Net Immutable Collections with WPF DataGrid

Has anybody found a nice pattern for binding ImmutableCollections ( from the MS BCL ) to a WPF DataGrid? To represent mutablility of an immutable structure I would wrap it with ISubject to track changes and provide new versions to the datagrid.
ISubject<ImmutableList<T>> <--(binding)--> DataGrid
The DataGrid obviously needs a mutable collection such as ObservableCollection directly under it so perhaps the problem can be reduced to
ISubject<ImmutableList<T>> <-----> ObservableCollection<T>
Any suggestions?
ObservableCollection
Here is a partial solution.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Immutable;
using System.Collections.Specialized;
using ReactiveUI;
namespace ReactiveUI.Ext
{
public class ImmutableListToReactive<T> : ReactiveObject, ICollection<T>, INotifyCollectionChanged, IDisposable, IList<T>
where T : class, Immutable
{
private ISubject<ImmutableList<T>> _Source;
ImmutableList<T> _Current;
public ImmutableList<T> Current
{
get { return _Current; }
set { this.RaiseAndSetIfChanged(ref _Current, value); }
}
public void Dispose()
{
_Subscription.Dispose();
}
public ImmutableListToReactive( ISubject<ImmutableList<T>> source )
{
_Source = source;
_Subscription = source.Subscribe(newVersion =>
{
if ( !rebound )
{
_Current = newVersion;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
});
}
private void OnNext( ImmutableList<T> list )
{
rebound = true;
_Current = list;
try
{
_Source.OnNext(list);
}
finally
{
rebound = false;
}
}
public void Add( T item )
{
OnNext(_Current.Add(item));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<T>(){item}, Current.Count - 1));
}
public void Clear()
{
OnNext(_Current.Clear());
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public bool Contains( T item )
{
return _Current.Contains(item);
}
public void CopyTo( T[] array, int arrayIndex )
{
_Current.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _Current.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove( T item )
{
var idx = _Current.IndexOf(item);
var next = _Current.Remove(item);
if ( next == _Current )
{
return false;
}
else
{
OnNext(next);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, idx));
return true;
}
}
public IEnumerator<T> GetEnumerator()
{
return _Current.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _Current.GetEnumerator();
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
private IDisposable _Subscription;
bool rebound = false;
protected virtual void OnCollectionChanged( NotifyCollectionChangedEventArgs e )
{
if ( !rebound )
{
rebound = true;
if ( CollectionChanged != null )
{
CollectionChanged(this, e);
}
rebound = false;
}
}
public int IndexOf( T item )
{
return _Current.IndexOf(item);
}
public void Insert( int index, T item )
{
OnNext(_Current.Insert(index, item));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
public void RemoveAt( int index )
{
var itemToBeRemoved = _Current[index];
OnNext(_Current.RemoveAt(index));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemToBeRemoved, index));
}
public T this[int index]
{
get
{
return _Current[index];
}
set
{
OnNext(_Current.SetItem(index, value));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, value, index));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value, index));
}
}
}
}
and a test case
using FluentAssertions;
using ReactiveUI.Subjects;
using System;
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Xunit;
namespace ReactiveUI.Ext.Spec
{
public class Data : Immutable
{
public int A { get; private set; }
public int B { get; private set; }
public bool Equals(Data other){
return A == other.A && B == other.B;
}
public bool Equals(object o){
Data other = o as Data;
if ( other == null )
{
return false;
}
return this.Equals(other);
}
public static bool operator ==( Data a, Data b )
{
return a.Equals(b);
}
public static bool operator !=( Data a, Data b )
{
return !a.Equals(b);
}
public Data( int a, int b )
{
A = a;
B = b;
}
}
public class DataMutable : ReactiveObject, IDisposable
{
int _A;
public int A
{
get { return _A; }
set { this.RaiseAndSetIfChanged(ref _A, value); }
}
int _B;
public int B
{
get { return _B; }
set { this.RaiseAndSetIfChanged(ref _B, value); }
}
IDisposable _Subscriptions;
public DataMutable( ILens<Data> dataLens )
{
_Subscriptions = new CompositeDisposable();
var d0 = dataLens.Focus(p => p.A).TwoWayBindTo(this, p => p.A);
var d1 = dataLens.Focus(p => p.B).TwoWayBindTo(this, p => p.B);
_Subscriptions = new CompositeDisposable(d0, d1);
}
public void Dispose()
{
_Subscriptions.Dispose();
}
}
public class ImmutableListToReactiveSpec : ReactiveObject
{
ImmutableList<Data> _Fixture;
public ImmutableList<Data> Fixture
{
get { return _Fixture; }
set { this.RaiseAndSetIfChanged(ref _Fixture, value); }
}
[Fact]
public void ReactiveListSux()
{
var a = new ReactiveList<int>();
var b = a.CreateDerivedCollection(x => x);
a.Add(10);
b.Count.Should().Be(1);
}
[Fact]
public void ShouldWork()
{
Fixture = ImmutableList<Data>.Empty;
// Convert an INPC property to a subject
ISubject<ImmutableList<Data>> s = this.PropertySubject(p => p.Fixture);
var MutableList = new ImmutableListToReactive<Data>(s);
var DerivedList = MutableList.CreateDerivedCollection(x => x);
Fixture = Fixture.Add(new Data(10, 20));
DerivedList.ShouldAllBeEquivalentTo(Fixture);
Fixture = Fixture.Add(new Data(11, 21));
DerivedList.ShouldAllBeEquivalentTo(Fixture);
Fixture = Fixture.Add(new Data(12, 22));
MutableList.Count.Should().Be(3);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
MutableList.Add(new Data(33, 88));
MutableList.Count.Should().Be(4);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
MutableList[1] = new Data(99, 21);
MutableList.Count.Should().Be(4);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
var itemAtOne = MutableList[1];
MutableList.RemoveAt(1);
MutableList.Should().NotContain(itemAtOne);
MutableList.Count.Should().Be(3);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
var i = new Data(78, 32);
MutableList.Insert(0, i);
DerivedList[0].Should().Be(i);
MutableList.Count.Should().Be(4);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
var j = new Data(18, 22);
MutableList.Insert(3, j);
DerivedList[3].Should().Be(j);
MutableList.Count.Should().Be(5);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
var k = new Data(18, 22);
MutableList.Add(k);
DerivedList[DerivedList.Count-1].Should().Be(k);
MutableList.Count.Should().Be(6);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
MutableList.Remove(i);
DerivedList[DerivedList.Count-1].Should().Be(k);
MutableList.Count.Should().Be(5);
DerivedList.ShouldAllBeEquivalentTo(Fixture);
MutableList.ShouldAllBeEquivalentTo(Fixture);
}
}
}

Using LINQ: How To Return Array Of Properties from a Class Collection?

Here is a Basic Class with TheProperty in question:
class BasicClass {
public BasicClass() {
TheProperty = new Object();
Stamped = DateTime.Now;
}
public object TheProperty { get; set; }
public DateTime Stamped { get; private set; }
}
Here is the Basic List:
class BasicList {
private List<BasicClass> list;
public BasicList() {
list = new List<BasicClass>();
}
public BasicClass this[object obj] {
get { return list.SingleOrDefault(o => o.TheProperty == obj); }
}
public void Add(BasicClass item) {
if (!Contains(item.TheProperty)) {
list.Add(item);
}
}
public bool Contains(object obj) {
return list.Any(o => o.TheProperty == obj); // Picked this little gem up yesterday!
}
public int Count { get { return list.Count; } }
}
I'd like to add a class to BasicList that will return an array of items.
I could write it like this, using traditional C#:
public object[] Properties() {
var props = new List<Object>(list.Count);
foreach (var item in list) {
props.Add(item.TheProperty);
}
return props.ToArray();
}
...but how would I write that using a LINQ or Lambda query?
return list.Select(p=>p.TheProperty).ToArray()

Finding the root nodes of all the of a tree from a nodes in any generic list

This is a entity and i want to list all the children node for a given node in a generic function
public static List<T> BuildTree<T>(List<T> list, T selectNode string keyPropName, string parentPropName, string levelPropName, int level = 0)
{
List<T> entity = new List<T>();
foreach (T item in list)
{
}
return entity;
}
example of the entity structure
protected long _coakey;
protected long _parentkey;
protected string _coacode;
protected string _coacodeclient;
protected string _coaname;
protected int _coalevel;
[DataMember]
public long coakey
{
get { return _coakey; }
set { _coakey = value; this.OnChnaged(); }
}
[DataMember]
public long parentkey
{
get { return _parentkey; }
set { _parentkey = value; this.OnChnaged(); }
}
[DataMember]
public string coacode
{
get { return _coacode; }
set { _coacode = value; this.OnChnaged(); }
}
[DataMember]
public string coacodeclient
{
get { return _coacodeclient; }
set { _coacodeclient = value; this.OnChnaged(); }
}
[DataMember]
public string coaname
{
get { return _coaname; }
set { _coaname = value; this.OnChnaged(); }
}
[DataMember]
public int coalevel
{
get { return _coalevel; }
set { _coalevel = value; this.OnChnaged(); }
}
Your BuildTree<T> method cannot determine the structure of the tree unless it knows something about its structure. At a very minimum, I would suggest making a base class or interface that defines a tree node, and then change the BuildTree method to work specifically with those types of objects. Then, it will be able to figure out the tree structure. Each of you entity classes would have to implement that tree node interface or inherit from the tree node base class. For instance:
public abstract class TreeNodeBase
{
public long parentkey
{
get { return _parentkey; }
set { _parentkey = value; this.OnChanged(); }
}
protected long _parentkey;
}
public class MyEntityTreeNode : TreeNodeBase
{
public long coakey
{
get { return _coakey; }
set { _coakey = value; this.OnChanged(); }
}
protected long _coakey;
// etc...
}
// Note the generic type constraint at the end of the next line
public static List<T> BuildTree<T>(List<T> list, T selectNode, string keyPropName, string parentPropName, string levelPropName, int level) where T : TreeNodeBase
{
List<T> entity = new List<T>();
foreach (TreeNodeBase node in list)
{
long parentKey = node.parentkey;
// etc...
}
return entity;
}
Node class:
public class Node<TKey, TValue> where TKey : IEquatable<TKey>
{
public TKey Key { get; set; }
public TKey ParentKey { get; set; }
public TValue Data { get; set; }
public readonly List<Node<TKey, TValue>> Children = new List<Node<TKey, TValue>>();
}
TreeBuilder:
public static Node<TKey, TValue> BuildTree<TKey, TValue>(IEnumerable<Node<TKey, TValue>> list,
Node<TKey, TValue> selectNode)
where TKey : IEquatable<TKey>
{
if (ReferenceEquals(selectNode, null))
{
return null;
}
var selectNodeKey = selectNode.Key;
foreach (var childNode in list.Where(x => x.ParentKey.Equals(selectNodeKey)))
{
selectNode.Children.Add(BuildTree(list, childNode));
}
return selectNode;
}
Usage:
List<MyClass> list = ...
var nodes = list.Select(x => new Node<long, MyClass>
{
Key = x.MyKey,
ParentKey = x.MyParentKey,
Data = x
}).ToList();
var startNode = nodes.FirstOrDefault(x => x.Data.Stuff == "Pick me!");
var tree = BuildTree(nodes, startNode);
MyClass example:
public class MyClass
{
public long MyKey;
public long MyParentKey;
public string Name;
public string Text;
public string Stuff;
}
I have solved it my self hope it help you
public static List<T> BuildTree<T>(List<T> list, T selectedNode, string keyPropName, string parentPropName, int endLevel = 0, int level = 0)
{
List<T> entity = new List<T>();
Type type = typeof(T);
PropertyInfo keyProp = type.GetProperty(keyPropName);
string _selectedNodekey = keyProp.GetValue(selectedNode, null).ToString();
PropertyInfo parentProp = type.GetProperty(parentPropName);
foreach (T item in list)
{
string _key = keyProp.GetValue(item, null).ToString();
string _parent = parentProp.GetValue(item, null).ToString();
if (_selectedNodekey == _parent)
{
T obj = (T)Activator.CreateInstance(typeof(T));
obj = item;
entity.Add(obj);
if (level == endLevel && level!=0) break;
entity.AddRange(BuildTree<T>(list, obj, keyPropName, parentPropName, level + 1));
}
}
return entity;
}

Categories

Resources