I would like to recursively build an complex object.
public class Name
{
public string firstName {get;set;}
public string lastName {get;set;}
}
public class Address
{
public string city {get;set;}
public string state {get;set;}
public string street {get;set;}
public string zip {get;set;}
}
public class Customer
{
public Name customerName {get;set;}
public Address customerAddress {get;set;}
public Guid id {get;set;}
}
Lets say that Customer lives in an assembly that I'm loading on the fly :)
I want to instantiate a type of Customer and populate its properties.
Customer object has more custom objects and a Guid property. How can I use recursion to create the Customer object and its nested objects. I have some code below where I stumbled into the fact that I should be using recursion.
static object TraversePropertyInfo(object obj, Assembly assembly)
{
Console.WriteLine(obj.GetType().Name);
foreach(PropertyInfo pi in obj.GetType().GetProperties())
{
if(pi.PropertyType.IsClass && pi.PropertyType.Namespace != "System")
{
if(pi.PropertyType.UnderlyingSystemType.GenericTypeArguments.Count() > 0)
{
Console.WriteLine("\tIList<{0}>", pi.Name);
}
else
{
Console.WriteLine("\t{0}\t<class>", pi.Name);
object child = Activator.CreateInstance(assembly.GetType(pi.PropertyType.FullName)); // create the child instance
obj.GetType().GetProperty(pi.Name).SetValue(obj, child); // set the child on the parent
// but the child can have children...
// I should be using recurrsion here
}
}
else
{
Console.WriteLine("\t{0}\t{1}", pi.Name, pi.PropertyType);
}
}
return obj;
}
void Main()
{
Create<Customer>().Dump();
}
// Define other methods and classes here
public class Name
{
public string Firstname { get; set; }
public string Lastname { get; set; }
}
public class Address
{
public string City { get; set; }
public string State { get; set; }
public string Street { get; set; }
public string Zip { get; set; }
}
public class Customer
{
public Name CustomerName { get; set; }
public Address CustomerAddress { get; set; }
public Guid Id { get; set; }
}
public static T Create<T>()
{
var type = typeof(T);
return (T)Create(type);
}
public static object Create(Type type)
{
var obj = Activator.CreateInstance(type);
foreach(var property in type.GetProperties())
{
var propertyType = property.PropertyType;
if (propertyType.IsClass
&& string.IsNullOrEmpty(propertyType.Namespace)
|| (!propertyType.Namespace.Equals("System")
&& !propertyType.Namespace.StartsWith("System.")))
{
var child = Create(propertyType);
property.SetValue(obj, child);
}
}
return obj;
}
Maybe this will work:
static object TraversePropertyInfo(object obj, Assembly assembly)
{
Console.WriteLine(obj.GetType().Name);
// we stop the iteration when we reached the root-class "object"
// which won´t add any custom properties
if (obj.GetType() == typeof(object) return obj;
foreach(PropertyInfo pi in obj.GetType().GetProperties())
{
if(pi.PropertyType.IsClass && pi.PropertyType.Namespace != "System")
{
if(pi.PropertyType.UnderlyingSystemType.GenericTypeArguments.Count() > 0)
{
Console.WriteLine("\tIList<{0}>", pi.Name);
}
else
{
Console.WriteLine("\t{0}\t<class>", pi.Name);
object child = Activator.CreateInstance(assembly.GetType(pi.PropertyType.FullName)); // create the child instance
child = TraversePropertyInfo(child, child.GetType().Assembly);
obj.GetType().GetProperty(pi.Name).SetValue(obj, child); // set the child on the parent
// this will do the recursion
return obj;
}
}
else
{
Console.WriteLine("\t{0}\t{1}", pi.Name, pi.PropertyType);
}
}
return obj;
}
Related
I am writing a method for extracting all properties from an object (including properties of its own) with custom attribute . For example
public class SomeModel
{
[Custom]
public string Name { get; set; }
public string TestData { get; set; }
[Custom]
public string Surname { get; set; }
public InnerModel InnerModel { get; set; }
}
And Inner Model :
public class InnerModel
{
public string Id { get; set; } = "TestID";
[Custom]
public string Year { get; set; }
public ThirdObject HidedObject { get; set; }
}
And the third one :
public class ThirdObject
{
[Custom]
public string HidedName { get; set; }
}
I need to find all properties with "Custom" attribute .
Testing :
SomeModel model = new SomeModel()
{
Name = "farid",
Surname = "Ismayilzada",
TestData = "Test" ,
InnerModel = new InnerModel() { Year ="2022" , HidedObject= New ThirdObject{ HidedName="Secret"}}
};
I need to write the method
GetMyProperties(model) => List<PropInf>()
[PropertyName= Name,Value=Farid ,Route="Name" ]
[PropertyName= Surname,Value=Ismayilzada,Route="Surname" ]
[PropertyName= Year,Value=2022,Route="InnerModel.Year" ]
[PropertyName= HidedName,Value=Secret,Route="InnerModel.HidedObject.HidedName" ]
How to get this information ?
You can write a method like this :
private static IEnumerable<PropInfo> GetPropertiesInfo(object obj, string route = "")
{
List<PropInfo> results = new List<PropInfo>();
// You can filter wich property you want https://learn.microsoft.com/en-us/dotnet/api/system.reflection.propertyinfo?view=net-6.0
var objectProperties = obj.GetType().GetProperties().Where(p => p.CanRead);
foreach (var property in objectProperties)
{
var value = property.GetValue(obj);
if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
results.AddRange(GetPropertiesInfo(value, route + property.Name + "."));
}
else
{
// Check if the property has the Custom Attribute
var customAttributes = property.GetCustomAttributes<CustomAttribute>();
if (!customAttributes.Any())
continue;
// You can set a method in your Attribute : customAttributes.First().CheckIfNeedToStoreProperty(obj);
results.Add(new PropInfo()
{
PropertyName = property.Name,
Value = value,
Route = route + property.Name
});
}
}
return results;
}
public class PropInfo
{
public string PropertyName { get; set; }
public object Value { get; set; }
public string Route { get; set; }
}
public class CustomAttribute : Attribute
{
public bool CheckIfNeedToStoreProperty(object obj)
{
return true;
}
}
This question already has answers here:
Recursively Get Properties & Child Properties Of A Class
(5 answers)
Closed 2 years ago.
I am trying to write an universal search to use for all objects.
I have this code, which is working fine to search in just one object's properties, but I would also like to search also in properties in related objects.
Eg. I have these Models/Objects
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address{ get; set; }
public ICollection<Contract> Contracts { get; set; }
}
public class Contract
{
public int Id { get; set; }
public DateTime From{ get; set; }
public DateTime To{ get; set; }
public string Comment{ get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
}
and I want to search if any of properties contains some a string eg. "Peter", I will call it this way:
string searchString = "Peter";
var customers = db.Customers
.Include(x => x.Contracts)
.WhereAnyPropertiesOfSimilarTypeContains(searchString);
this code will check if any properties of 'Customer' contains string "Peter".
But I would also need to check if the related model 'Contract' contains "Peter.
public static class EntityHelper
{
public static IQueryable<TEntity> WhereAnyPropertiesOfSimilarTypeContains<TEntity, TProperty>(this IQueryable<TEntity> query, TProperty value)
{
var param = Expression.Parameter(typeof(TEntity));
var predicate = PredicateBuilder.False<TEntity>(); //--- True to equal
var entityFields = GetEntityFieldsToCompareTo<TEntity, TProperty>();
foreach (var fieldName in entityFields)
{
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var predicateToAdd = Expression.Lambda<Func<TEntity, bool>>(
Expression.Call(
Expression.PropertyOrField(param, fieldName), method,
Expression.Constant(value)), param);
predicate = predicate.Or(predicateToAdd); //--- And to equal
}
return query.Where(predicate);
}
// TODO: You'll need to find out what fields are actually ones you would want to compare on.
// This might involve stripping out properties marked with [NotMapped] attributes, for
// for example.
public static IEnumerable<string> GetEntityFieldsToCompareTo<TEntity, TProperty>()
{
Type entityType = typeof(TEntity);
Type propertyType = typeof(TProperty);
var fields = entityType.GetFields()
.Where(f => f.FieldType == propertyType)
.Select(f => f.Name);
var properties = entityType.GetProperties()
.Where(p => p.PropertyType == propertyType)
.Select(p => p.Name);
return fields.Concat(properties);
}
}
Thanks.
After reread the question. I don't know what are you trying, but here I put the idea I have what are you looking for.
public class Customer : AbstractEntity
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public ICollection<Contract> Contracts { get; set; }
}
public class Contract : AbstractEntity
{
//what property here can be string "Peter"? Comments?
//what are you trying?
public int Id { get; set; }
public DateTime From { get; set; }
public DateTime To { get; set; }
public string Comment { get; set; }
public int CustomerId { get; set; }
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
}
public abstract class AbstractEntity
{
//this method can be used to preselect properties you want
protected virtual Tuple<bool, ICollection<PropertyInfo>> PropertyCollector()
{
return new Tuple<bool, ICollection<PropertyInfo>>(false, null);
}
public IEnumerable<Tuple<Type, object>> GetRowValues()
{
foreach (var prop in GetRows())
{
yield return new Tuple<Type, object>(prop.PropertyType, prop.GetValue(this));
}
}
public ICollection<PropertyInfo> GetRows()
{
var tuple = PropertyCollector();
ISet<PropertyInfo> pInfo;
if (tuple.Item1)
{
pInfo = new HashSet<PropertyInfo>(tuple.Item2);
}
else //search all non virtual, private, protected properties, "following POCO scheme"
{
pInfo = new HashSet<PropertyInfo>();
foreach (var prop in GetType().GetProperties())
{
foreach (var access in prop.GetAccessors())
{
if ((!access.IsVirtual && !access.IsPrivate) && (prop.CanWrite && prop.CanRead))
{
pInfo.Add(prop);
}
}
}
}
return pInfo;
}
}
public static class Searchs
{
public static ICollection<object> ObjectsWithStringFound(ICollection<Customer> customers, string toBeFound)
{
var objs = new List<object>();
foreach (var cust in customers)
{
var strings = cust.GetRowValues().Where(tpl => tpl.Item1 == typeof(string)).Select(tpl => tpl.Item2);
var contracts = cust.GetRowValues().Where(tpl => tpl.Item2 is IEnumerable<Contract>).Select(tpl => tpl.Item2);
if (strings.Any(str => str == toBeFound))
{
objs.Add(cust);
}
else if (contracts.Any(ctr => ((IEnumerable<Contract>)ctr).!!!!!!!!! == toBeFound))
{ //What I suppose I must "match" with "Peter"??!?!
objs.Add(contracts.First(ctr => ((IEnumerable<Contract>)ctr).!!!!!!!!! == toBeFound));
}
}
return objs;
}
}
I think we aren't understanding each other.
I have following two classes
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
I have an instance of Employee class as follows.
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
I have a method which gets the property value based on the property name as follows:
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
In the above method,when I try to get property value of nested class like
GetPropertyValue(employee, "employee.Child.ChildName")
or
GetPropertyValue(GetPropertyValue(employee, "Family"), "ChildName"
doesn't return any value because type.GetRuntimeProperty(part) is always null.
Is there any way to fix this problem?
You problem lies in this line:
foreach (String part in nameParts)
Because you are iterating over each part of nameParts, you are also iterating over "employee", which of course is not a valid property.
Try either this:
foreach (String part in nameParts.Skip(1))
Or calling the method like this:
GetPropertyValue(employee, "Child.ChildName")
(Notice no "employee.", because you already pass in an employee)
The problem in this case is that when you split the string employee.Child.ChildName, the "employee" is the first part. However, employee is not a property of the source i.e. Employee Class.
Try this:
public class Program
{
public static void Main()
{
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
GetPropertyValue(employee, "employee.Child.ChildName");
}
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
nameParts = nameParts.Skip(1).ToArray();
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
Here, i have skipped the first part of the string i.e. "employee". However, you can solve the problem by passing Child.ChildName
This question is around 2 years old, but I found a another working solution for you question, which is easy to understand. If you initialize the object in calling calss constructor you can use dot(.) notation to assign or read property. Example -
public class Family{
public string ChildName { get; set; }
}
public class Employee{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
public Employee(){
Child = new Family();
}
}
Employee emp = new Employee();
emp.Family.ChildName = "Nested calss attribute value";
I have a DTO that looks like this:
public class MyDto
{
[MyAttribute("attribute1")]
public string Property1 {get;set;}
[MyAttribute("attribute2")]
public string Property2 {get;set;}
}
If I have the string "attribute1", how do I use that to get to the value of Property1 in an instance of MyDto?
Use Reflection. Unfortunately, there's no way to obtain the property from an attribute: you have to iterate through each property and check its attribute.
Not the most robust code, but try this:
public class MyAttributeAttribute : Attribute
{
public MyAttributeAttribute(string value)
{
Value=value;
}
public string Value { get; private set; }
}
public class MyDto
{
[MyAttribute("attribute1")]
public string Property1 { get; set; }
[MyAttribute("attribute2")]
public string Property2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
MyDto dto=new MyDto() { Property1="Value1", Property2="Value2" };
string value=GetValueOf<string>(dto, "attribute1");
// value = "Value1"
}
public static T GetValueOf<T>(MyDto dto, string description)
{
if(string.IsNullOrEmpty(description))
{
throw new InvalidOperationException();
}
var props=typeof(MyDto).GetProperties().ToArray();
foreach(var prop in props)
{
var atts=prop.GetCustomAttributes(false);
foreach(var att in atts)
{
if(att is MyAttributeAttribute)
{
string value=(att as MyAttributeAttribute).Value;
if(description.Equals(value))
{
return (T)prop.GetValue(dto, null);
}
}
}
}
return default(T);
}
}
Ok so at first I thought this was easy enough, and maybe it is and I'm just too tired - but here's what I'm trying to do. Say I have the following objects:
public class Container
{
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<Telephone> Telephones { get; set; }
}
public class Telephone
{
public string CellPhone { get; set; }
}
What I need to be able to do, is 'flatten' Containers property names in to a string (including ALL child properties AND child properties of child properties) that would look something like this:
Container.Name, Container.Addresses.AddressLine1, Container.Addresses.AddressLine2, Container.Addresses.Telephones.CellPhone
Does that make any sense? I can't seem to wrap it around my head.
I suggest you to mark all the classes, you need to grab, with custom attribute after that you could do something like this
class Program
{
static void Main(string[] args)
{
var lines = ExtractHelper.IterateProps(typeof(Container)).ToArray();
foreach (var line in lines)
Console.WriteLine(line);
Console.ReadLine();
}
}
static class ExtractHelper
{
public static IEnumerable<string> IterateProps(Type baseType)
{
return IteratePropsInner(baseType, baseType.Name);
}
private static IEnumerable<string> IteratePropsInner(Type baseType, string baseName)
{
var props = baseType.GetProperties();
foreach (var property in props)
{
var name = property.Name;
var type = ListArgumentOrSelf(property.PropertyType);
if (IsMarked(type))
foreach (var info in IteratePropsInner(type, name))
yield return string.Format("{0}.{1}", baseName, info);
else
yield return string.Format("{0}.{1}", baseName, property.Name);
}
}
static bool IsMarked(Type type)
{
return type.GetCustomAttributes(typeof(ExtractNameAttribute), true).Any();
}
public static Type ListArgumentOrSelf(Type type)
{
if (!type.IsGenericType)
return type;
if (type.GetGenericTypeDefinition() != typeof(List<>))
throw new Exception("Only List<T> are allowed");
return type.GetGenericArguments()[0];
}
}
[ExtractName]
public class Container
{
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}
[ExtractName]
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<Telephone> Telephones { get; set; }
}
[ExtractName]
public class Telephone
{
public string CellPhone { get; set; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = true)]
public sealed class ExtractNameAttribute : Attribute
{ }
Per my comment, you could use something like this if it will always be a generic List type that you want to link to a child type. IteratePropertiesRecursively is an iterator over the properties of the given type, that will recursively enumerate the properties of the type and all child types linked through a generic List.
protected void Test()
{
Type t = typeof(Container);
string propertyList = string.Join(",", IteratePropertiesRecursively("", t).ToArray<string>());
// do something with propertyList
}
protected IEnumerable<string> IteratePropertiesRecursively(string prefix, Type t)
{
if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith(".")) prefix += ".";
prefix += t.Name + ".";
// enumerate the properties of the type
foreach (PropertyInfo p in t.GetProperties())
{
Type pt = p.PropertyType;
// if property is a generic list
if (pt.Name == "List`1")
{
Type genericType = pt.GetGenericArguments()[0];
// then enumerate the generic subtype
foreach (string propertyName in IteratePropertiesRecursively(prefix, genericType))
{
yield return propertyName;
}
}
else
{
// otherwise enumerate the property prepended with the prefix
yield return prefix + p.Name;
}
}
}
Note: This code will not correctly handle a type that recursively includes itself as a type of one of its properties. Trying to iterate over such a type will result in a StackOverflowException, as pointed out by #Dementic (thanks!).