How to add elements to IList collection using a function - c#

I'm just trying to understand an example of WPF treeview.
My goal ist to populate a treeview with some items, stored in a List.
This is the example:
public class Person
{
readonly List<Person> _children = new List<Person>();
public IList<Person> Children
{
get { return _children; }
}
public string Name { get; set; }
}
//Some added nodes:
public static Person GetFamilyTree()
{
// In a real app this method would access a database.
return new Person
{
Name = "David Weatherbeam",
Children =
{
new Person
{
Name="Alberto Weatherbeam",
Children=
{
new Person
{
Name="Zena Hairmonger",
Children=
{
new Person
{
Name="Sarah Applifunk",
}
}
},
new Person
{
Name="Jenny van Machoqueen",
Children=
{
new Person
{
Name="Nick van Machoqueen",
},
new Person
{
Name="Matilda Porcupinicus",
},
new Person
{
Name="Bronco van Machoqueen",
}
}
}
}
}
}
};
}
So far it works.
Now I'd like to replace the static persons unter the first parent node with a list that I created before, reading a file.
XDocument ecad = XDocument.Load(openFileDialog.FileName);
GlobalVar.persons = new List<Persons>(); //globally available list
Person person = null;
//Einlesen der XML-Datei in lokale Variable (Model)
foreach(XElement elem in ecad.Descendants("Variable"))
{
if (elem.Element("Name") != null)
{
person = new Person(){ Name = elem.Element("Name").Value };
persons.Add(person);
}
}
Now I'd like to add these Persons to a root-person
//GlobalVar.List<Persons> persons
public static Person GetFamilyTree()
{
return new Person{
Name = "Family",
Children = persons
}
}
So how can I replace the Children = new... with the function that reads the data from file?
I'm really confused

What I think is confusing, Children is a readonly property but with the "static Children" it works, with the IList it doesn't (because of readonly). Can someone explain the difference, please?
Your GetFamilyTree() method uses a nested object initializer. Please refer to Jon Skeet's answer here for more information.
If you intend to read data from some source and then set the Children property, you should add a setter to it. You can also remove the backing field:
public class Person
{
public IList<Person> Children { get; set; } = new List<Person>();
public string Name { get; set; }
}

Related

Build Dynamic query using Expression Tree

I am working on dynamic query building in LINQ using Expression Tree.
I have taken the reference to the following post
https://www.codeproject.com/Tips/582450/Build-Where-Clause-Dynamically-in-Linq
How can I build expression if I want to check all the element in the list contains in another collection or not?
I have a Person class
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
and I have a list
List<Person> personList = new List<Person>()
{
new Person{ Name = "Shekhar", Age = 31},
new Person{ Name = "Sandip", Age = 32},
new Person{ Name = "Pramod", Age = 32},
new Person{ Name = "Kunal", Age = 33}
};
I have another list
List<string> nameList = new List<string>() { "Sandip", "Prashant" };
How can I build expression tree to check all the element in the list "nameList" contains in "personList" and give result true or false?
try this:
public bool Find(List<string> nameList, List<Person> personList)
{
foreach (var name in nameList)
if (personList.FirstOrDefault(person => person.Name == name) != null)
{
// Find
return true;
}
return false;
}
try this:
bool contained = !personList.Select(l=>l.Name).Except(nameList).Any();

LINQ to Get All heirerichal children

I have been digging this quite a while.
public class Person
{
public string Name { get; set; }
public string Age { get; set; }
public List<Person> Children { get; set; }
}
I want a single LINQ query to find out "All the persons whose Age > 4 in this collection".
Note: You have to traverse Collection of Person + Collection of Children, so each children object will have a collection of Person till Children becomes null.
First i can't understand why all your properties private and Age is not int type. So my class looks like this:
public partial class Person
{
public string Name { get; set; }
public int Age { get; set; }
public List<Person> Childrens { get; set; }
}
Note partial word. This word will allow you to place your class logic in separate files and this could be usefull when you creating some custom logic in class.
Then I simply create this method in different file:
public partial class Person
{
public Person GetPersonWithChindren(int maxAge)
{
return new Person
{
Age = this.Age,
Name = this.Name,
Childrens = this.Childrens != null
? this.Childrens
.Where(x => x.Age < maxAge)
.Select(x => x.GetPersonWithChindren(maxAge)) //this line do recursive magic
.ToList()
: null
};
}
}
As you can see this method checking Age of each child and if Age is ok then it checks next level of hierarchy untill Childrens is null.
So you can use it like this:
var person = new Person()
{
//initialisation of your collection here
}
//result will contains only nodes where Person have age < 4 and Childs that have age < 4
var result = person.GetPersonWithChindren(4);
Note that this solution will work normal with linqToEntities. But if you using LinqToSQL this expression produces query to DB on each Person entity. So if you have many Persons and deep hierarhy it will costs you a lot of machine time. In that case you should to write stored procedure with CTE instead of LinQ query.
UPDATE:
You even can write more general solution with a help of Func<T> class like this:
public partial class Person
{
public Person GetPersonWithChindren(Func<Person, bool> func)
{
return new Person
{
Age = this.Age,
Name = this.Name,
Childrens = this.Childrens != null
? this.Childrens
.Where(x => func(x))
.Select(x => x.GetPersonWithChindren(func))
.ToList()
: null
};
}
}
And then you can use it like this:
var result = person.GetPersonWithChindren(x => x.Age < 4);
You always can change your criteria now where you want to use your function.
Create a visitor. In this example by implementing a helper class:
public static class Helpers
public static IEnumerable<Person> GetDescendants(this Person person)
{
foreach (var child in person.Children)
{
yield return child;
foreach (var descendant in child.GetDescendants())
{
yield return descendant;
}
}
}
this is one of the times where the "yield return many" would be useful.
If you're ensuring that .Children is automatically created, then this works:
Func<Person, Func<Person, bool>, Person> clone = null;
clone = (p, f) => f(p) ? new Person()
{
Name = p.Name,
Age = p.Age,
Children = p.Children.Select(c => clone(c, f)).Where(x => x != null).ToList(),
} : null;
var olderThan4 = clone(person, p => p.Age > 4);
Yes, that's it. Effectively three lines.
If you start with this data:
var person = new Person()
{
Name = "Fred", Age = 30,
Children = new List<Person>()
{
new Person() { Name = "Bob", Age = 7, },
new Person() { Name = "Sally", Age = 3, }
},
};
...then you get this result:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
private List<Person> _children = null;
public List<Person> Children
{
get
{
if (_children == null)
{
_children = new List<Person>();
}
return _children;
}
set
{
_children = value;
}
}
}

How to not serialize an object based on a property's value?

If the tags didn't give it away, I'm working with C#'s XmlSerializer class.
Say, for example, I have a Person class with various properties including age (int), name (string), and deceased (bool). Is there a way to specify that I don't want to serialize any objects whose deceased flags are true?
Edit: I should have specified, but unfortunately due to the situation I can't really edit my list of objects because it's a member of another class, which is what I'm actually serializing. Are there any other suggestions?
Assuming that you have following type of Class structure(As you specified in the comment)
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public bool Deceased { get; set; }
}
public class Being
{
public string Data { get; set; }
[XmlElement("Human")]
public Person Human { get; set; }
public bool ShouldSerializeHuman()
{
return !this.Human.Deceased;
}
}
Here I have added a method called ShouldSerialize this is called a pattern for XML serialization. Here you can use XmlArray and XmlArrayItem for lists etc.(With given name) then the ShouldSerialize checks if it can be serialized.
Below is the code I used for testing.
private static void Main(string[] args)
{
var livingHuman = new Person() { Age = 1, Name = "John Doe", Deceased = true };
var deadHuman = new Person() { Age = 1, Name = "John Doe", Deceased = false };
XmlSerializer serializer = new XmlSerializer(typeof(Being));
serializer.Serialize(Console.Out, new Being { Human = livingHuman, Data = "new" });
serializer.Serialize(Console.Out, new Being { Human = deadHuman, Data = "old" });
}
And here's the output:
=============================
Update:
If you have list of Person as Humans:
public class Being
{
// [XmlAttribute]
public string Data { get; set; }
// Here add the following attributes to the property
[XmlArray("Humans")]
[XmlArrayItem("Human")]
public List<Person> Humans { get; set; }
public bool ShouldSerializeHumans()
{
this.Humans = this.Humans.Where(x => !x.Deceased).ToList();
return true;
}
}
Sample Test:
private static void Main(string[] args)
{
var livingHuman = new Person() { Age = 1, Name = "John Doe", Deceased = true };
var deadHuman = new Person() { Age = 1, Name = "John Doe", Deceased = false };
var humans = new List<Person> { livingHuman, deadHuman };
XmlSerializer serializer = new XmlSerializer(typeof(Being));
serializer.Serialize(Console.Out, new Being() { Humans = humans, Data = "some other data" });
}
Output:
If you have a list of Person objects and only want to serialise some of them, then just filter out the ones you don't need. For example:
List<Person> people = GetPeople(); //from somewhere
List<Person> filteredPeople = people.Where(p => !p.Deceased);
Now you only need to serialise filteredPeople.

Adding values using LINQ to an object property with a Dictionary property

I have a dataset which returns a couple of contact information in string(Phone, mobile, skype). I created an object with a Dictionary property where i can put the contact information in a key value pair. The problem is, I am assigning the values of the object using Linq. Hope somebody can help. Here is my code:
public class Student
{
public Student()
{
MotherContacts = new ContactDetail();
FatherContacts = new ContactDetail();
}
public ContactDetail MotherContacts { get; set; }
public ContactDetail FatherContacts { get; set; }
}
public class ContactDetail
{
public ContactDetail()
{
Items = new Dictionary<ContactDetailType, string>();
}
public IDictionary<ContactDetailType, string> Items { get; set; }
public void Add(ContactDetailType type, string value)
{
if(!string.IsNullOrEmpty(value))
{
Items.Add(type, value);
}
}
}
public enum ContactDetailType
{
PHONE,
MOBILE
}
Here's how I assign value to the Student object:
var result = ds.Tables[0].AsEnumerable();
var insuranceCard = result.Select(row => new Student()
{
MotherContacts.Items.Add(ContactDetailType.PHONE, row.Field<string>("MotherPhone"),
MotherContacts.Items.Add(ContactDetailType.MOBILE, row.Field<string>("MotherMobile")
}).FirstOrDefault();
The compiler says that the MotherContacts is not recognized in the context. What should I do?
I think your code should look like:
var insuranceCard = result.Select(row =>
{
var s = new Student();
s.MotherContacts.Items.Add(ContactDetailType.PHONE, row.Field<string>("MotherPhone");
s.MotherContacts.Items.Add(ContactDetailType.MOBILE, row.Field<string>("MotherMobile");
return s;
}).FirstOrDefault();
You are using the object initializer syntax in a wrong way. The correct use is:
new Student{MotherContacts = value} where value must be a ContactDetail.

Why isn't TypeDescriptor.GetProperties() working for me?

I have some code that creates a couple of Person objects and then adds those objects to a List. I'm trying to output the properties and values to the console by using TypeDescriptor.GetProperties(). I'm not getting any output, though. When I step through in debug, the descriptor foreach loop just acts like there are no properties in my Person object. However, when I look at the object in debug, I can see all the properties for the object and they all have values (i.e. name = "Mark", age = "40", etc). Can someone tell me why it isn't working when, clearly, there are properties and values in my objects?
Here's the code that I'm using for this particular part:
foreach (var person in People)
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(person))
{
string name = descriptor.Name;
object value = descriptor.GetValue(person);
Console.WriteLine("{0}={1}", name, value);
}
}
EDIT: Here is the class and program I'm tinkering with. Basically just a program that let's you drag and drop a particular XML file to the form with the intent of printing the object's properties and their values to the console.
Here is my Person class:
namespace WindowsFormsApplication5
{
public class Person
{
//Must have zero parameter constructor for Object => XML serialization.
public Person(){}
public Person(string name, int age, string gender, bool hasChildren,
List<Person> children = null )
{
Name = name;
Age = age;
Gender = gender;
HasChildren = hasChildren;
Children = children;
}
private string Name { get; set; }
private int Age { get; set; }
private string Gender { get; set; }
private bool HasChildren { get; set; }
private List<Person> Children { get; set; }
}
}
Here is my Program:
namespace WindowsFormsApplication5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var People = new List<Person>();
var ChildrenList = new List<Person>();
var hasChildren = false;
var ofd = new OpenFileDialog { Filter = "XML|*.xml" };
if (ofd.ShowDialog() != DialogResult.OK) return;
var xdoc = XDocument.Load(ofd.FileName);
foreach (var element in xdoc.Descendants("People").Elements("Person"))
{
var name = element.Element("Name").Value;
var age = Convert.ToInt32(element.Element("Age").Value);
var gender = element.Element("Name").Value;
var xmlNode = element.Descendants("Children").FirstOrDefault(xe => xe.Elements("Child").Any());
if (xmlNode != null)
{
hasChildren = true;
foreach (var child in element.Descendants("Children").Descendants("Child"))
{
var ChildName = child.Element("Name").Value;
var ChildAge = Convert.ToInt32(child.Element("Age").Value);
var ChildGender = child.Element("Gender").Value;
ChildrenList.Add(new Person(ChildName, ChildAge, ChildGender, false));
}
}
People.Add(new Person(name, age, gender, hasChildren, ChildrenList));
}
foreach (var person in People)
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(person))
{
string name = descriptor.Name;
object value = descriptor.GetValue(person);
Console.WriteLine("{0}={1}", name, value);
}
}
}
}
}
The properties of your Person class are private; TypeDescriptor returns only public properties.

Categories