I'd like return an object (ExpandoObject) with only the fields and nested fields received by the method.
var fieldsToGet = new List<string> { "FirstName", "Id"};
When I do this :
.Select(x => Helpers.FilteringProperties(x, fieldsToGet))
I receive an object with this two values, that's work.
I receive an object with FirstName and Id
Now I'd like return some properties of the nested object :
var fieldsToGet = new List<string> { "FirstName", "Id", "Language.Name"};
I'd like receive these properties :
FirstName, Id and Language.Name
The code below works but I'd like stay generic enough and be able to manage the nested.
How can I do this generic, managed the nested object ?
Thanks,
The current code :
public static object FilteringProperties(object employee, List<string> fields)
{
if (!fields.Any())
return employee;
else
{
ExpandoObject result = new ExpandoObject();
foreach (var field in fields)
{
var fieldValue = employee.GetType()
.GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
.GetValue(employee, null);
((IDictionary<String, Object>)result).Add(field, fieldValue);
}
return result;
}
}
Sample classes :
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Language Language { get; set; }
public int LanguageId { get; set; }
}
public class Language
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
I suppose you could do a split on the "."-character, get the Property with that name, and then get its value by calling your own function recursively.
Something like this (pseudo-code, can be a lot better)
if (field.Contains(".")) {
var parts = field.Split('.');
var fieldName = parts[0];
List<string> toGet = new List<string>();
toGet.Add(parts[1]); // this now contains everything after the "."
var fieldValue = employee.GetType()
.GetProperty(fieldName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
.GetValue(employee, null);
((IDictionary<String, Object>)result).Add(field, FilteringProperties(fieldValue, toGet)
}
Using a recursive approach. I'm sure it can be improved.
public static object FilteringProperties(object employee, List<string> fields)
{
if (!fields.Any())
return employee;
else
{
ExpandoObject result = new ExpandoObject();
foreach (var field in fields)
{
object fieldValue = null;
Regex regex = new Regex("(\\w+)\\.(\\w+)");
Match match = regex.Match(field);
if (match.Success)
{
string className = match.Groups[1].Value;
string propertyName = match.Groups[2].Value;
var o = FilteringProperties(employee.GetType().GetProperty(className).GetValue(employee, null), new List<string>() {propertyName});
var entry = (IDictionary<string, object>) o;
fieldValue = entry[propertyName];
}
if(fieldValue == null)
fieldValue = employee.GetType()
.GetProperty(field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance)
.GetValue(employee, null);
((IDictionary<String, Object>)result).Add(field, fieldValue);
}
return result;
}
}
Something like this seems to work for me -
public static ExpandoObject CreateObject(object parent, List<string> fields)
{
var expando = new ExpandoObject();
var ret = (IDictionary<string,object>)expando;
foreach (var property in fields)
{
//split to determine if we are a nested property.
List<string> properties = property.Split('.').ToList();
if (properties.Count() > 1)
{
//get our 'childs' children - ignoring the first item
var grandChildren = properties.Skip(1);
//copy this child object and use it to pass back into this method recusivly - thus creating our nested structure
var child = parent.GetType().GetProperty(properties[0]).GetValue(parent, null);
//passing in the child object and then its children - which are grandchildren from our parent.
ret.Add(properties[0], CreateObject(child, grandChildren.ToList()));
}
else //no nested properties just assign the property
ret.Add(property, parent.GetType().GetProperty(property).GetValue(parent));
}
return expando;
}
Thanks to Marc - guru from this site - I came up with the following approach:
public static object GetFlattenPropertyValue(this object source, string flattenPropertyName) {
var expression = source.GetType().CreatePropertyExpression(flattenPropertyName);
if (expression != null) {
var getter = expression.Compile();
return getter.DynamicInvoke(source);
}
return null;
}
public static LambdaExpression CreatePropertyExpression(this Type type, string flattenPropertyName) {
if (flattenPropertyName == null) {
return null;
}
var param = Expression.Parameter(type, "x");
Expression body = param;
foreach (var member in flattenPropertyName.Split('.')) {
body = Expression.PropertyOrField(body, member);
}
return Expression.Lambda(body, param);
}
Related
Currently, am adding the properties and values to the object manually like this example and sending to Dapper.SimpleCRUD to fetch data from Dapper Orm. This is the desired output I would like to achieve.
object whereCriteria = null;
whereCriteria = new
{
CountryId = 2,
CountryName = "Anywhere on Earth",
CountryCode = "AOE",
IsActive = true
};
The following class should build the object in the above mentioned format and return the ready-made object.
public static class WhereClauseBuilder
{
public static object BuildWhereClause(object model)
{
object whereObject = null;
var properties = GetProperties(model);
foreach (var property in properties)
{
var value = GetValue(property, model);
//Want to whereObject according to the property and value. Need help in this part!!!
}
return whereObject;
}
private static object GetValue(PropertyInfo property, object model)
{
return property.GetValue(model);
}
private static IEnumerable<PropertyInfo> GetProperties(object model)
{
return model.GetType().GetProperties();
}
}
This function WhereClauseBuilder.BuildWhereClause(object model) should return the object in expected format (mentiond above). Here is the implementation of how I would like to use.
public sealed class CountryModel
{
public int CountryId { get; set; }
public string CountryName { get; set; }
public string CountryCode { get; set; }
public bool IsActive { get; set; }
}
public class WhereClauseClass
{
public WhereClauseClass()
{
var model = new CountryModel()
{
CountryCode = "AOE",
CountryId = 2,
CountryName = "Anywhere on Earth",
IsActive = true
};
//Currently, won't return the correct object because the implementation is missing.
var whereClauseObject = WhereClauseBuilder.BuildWhereClause(model);
}
}
Maybe something like that:
private const string CodeTemplate = #"
namespace XXXX
{
public class Surrogate
{
##code##
}
}";
public static Type CreateSurrogate(IEnumerable<PropertyInfo> properties)
{
var compiler = new CSharpCodeProvider();
var compilerParameters = new CompilerParameters { GenerateInMemory = true };
foreach (var item in AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic))
{
compilerParameters.ReferencedAssemblies.Add(item.Location);
}
var propertiesCode =
string.join("\n\n", from pi in properties
select "public " + pi.PropertyType.Name + " " + pi.Name + " { get; set; }");
var source = CodeTemplate.Replace("##code##", propertiesCode);
var compilerResult = compiler.CompileAssemblyFromSource(compilerParameters, source);
if (compilerResult.Errors.HasErrors)
{
throw new InvalidOperationException(string.Format("Surrogate compilation error: {0}", string.Join("\n", compilerResult.Errors.Cast<CompilerError>())));
}
return compilerResult.CompiledAssembly.GetTypes().First(x => x.Name == "Surrogate");
}
And now use it:
public static object BuildWhereClause(object model)
{
var properties = GetProperties(model);
var surrogateType = CreateSurrogate(properties);
var result = Activator.CreateInstance(surrogateType);
foreach (var property in properties)
{
var value = GetValue(property, model);
var targetProperty = surrogateType.GetProperty(property.Name);
targetProperty.SetValue(result, value, null);
}
return result;
}
I didn't compile that. It's only written here. Maybe there are some errors. :-)
EDIT:
To use ExpandoObject you can try this:
public static object BuildWhereClause(object model)
{
var properties = GetProperties(model);
var result = (IDictionary<string, object>)new ExpandoObject();
foreach (var property in properties)
{
var value = GetValue(property, model);
result.Add(property.Name, value);
}
return result;
}
But I don't know whether this will work for you.
Is there an automagic (automapper?) way to map an entity to a runtime created dynamic object with properties passed as parameters? I want to do an API where the clients can select the properties of the entities they want to fetch.
I mean:
class Patient
{
public int PatientId{ get; set; }
public string Name{ get; set; }
public string LastName{ get; set; }
public string Address{ get; set; }
...
}
getPatient(string[] properties)
{
//Return an object that has the properties passed as parameters
}
Imagine you only want to fetch a PatientDTO with PatientId and Name:
getPatient(new string[]{"PatientId", "Name"}
should return
{
"PatientId": "1234",
"Name": "Martin",
}
and so on.
For now I'm doing it with Dictionary, but probably there is a better way. This is my approach:
For a single object:
public static Dictionary<string, object> getDTO(string[] properties, T entity)
{
var dto = new Dictionary<string, object>();
foreach (string s in properties)
{
dto.Add(s, typeof(T).GetProperty(s).GetValue(entity));
}
return dto;
}
For a list of objects:
public static List<Dictionary<string, object>> getDTOList(string[] properties, List<T> TList)
{
var dtoList = new List<Dictionary<string, object>>();
foreach(T entity in TList)
{
dtoList.Add(getDTO(properties, entity));
}
return dtoList;
}
Thank you.
How about creating a new dynamic object based solely on the specified property fields and returning the dynamic?
You will need to add using statements for: System.Dynamic and System.ComponentModel for the following to work.
public static dynamic getDTO(object entity, string[] properties)
{
IDictionary<string, object> expando = new ExpandoObject();
foreach (var p in properties)
{
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(entity.GetType()))
{
if (property.Name == p)
{
expando.Add(p, property.GetValue(entity));
break;
}
}
}
return expando as ExpandoObject;
}
Calling this method would look something like this:
var patient = new Patient() { PatientId = 1, Name = "Joe", LastName = "Smith", Address = "123 Some Street\nIn Some Town, FL 32333" };
var propList = new string[] { "PatientId", "Name", "Address" };
dynamic result = getDTO(patient, propList);
Console.WriteLine("Id:{0} Name: {1}\nAddress: {2}", result.PatientId, result.Name, result.Address);
Console.ReadLine();
This question already has answers here:
How to recursively print the values of an object's properties using reflection
(4 answers)
Closed 9 years ago.
I came over this site (http://snipplr.com/view.php?codeview&id=17637), which illustrates use of reflection like this:
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
private void button2_Click_1(object sender, EventArgs e)
{
var person = new Person { Age = 30, Name = "Tony Montana" };
var properties = typeof(Person).GetProperties();
foreach (PropertyInfo property in properties)
{
Console.WriteLine("{0} = {1}", property.Name, property.GetValue(person, null));
}
}
The codesnippet above will give you:
Age: 30
Name: Tony Montana
What if we added "Kid" to the class "AnotherPerson" like this
public class Kid
{
public int KidAge { get; set; }
public string KidName { get; set; }
}
public class AnotherPerson
{
public int Age { get; set; }
public string Name { get; set; }
public Kid Kid { get; set; }
}
This snippet;
private void button3_Click(object sender, EventArgs e)
{
var anotherPerson = new AnotherPerson { Age = 30, Name = "Tony Montana", Kid = new Kid { KidAge = 10, KidName = "SomeName" } };
var properties = typeof(AnotherPerson).GetProperties();
foreach (PropertyInfo property in properties)
{
Console.WriteLine("{0} = {1}", property.Name, property.GetValue(anotherPerson, null));
}
}
gives me:
Age: 30
Name: Tony Montana
Kid: ProjectName.Form1+Kid
Not quite what I was looking for.... Could I use reflection to iterate trough "Kid" also? Suggestions?
Could have a helper method which is something like this?
public List<KeyValuePair<string, object>> GetPropertiesRecursive(object instance)
{
List<KeyValuePair<string, object>> propValues = new List<KeyValuePair<string, object>>();
foreach(PropertyInfo prop in instance.GetType().GetProperties())
{
propValues.Add(new KeyValuePair<string, object>(prop.Name, prop.GetValue(instance, null));
propValues.AddRange(GetPropertiesRecursive(prop.GetValue(instance));
}
}
Call as - GetPropertiesRecursive(anotherPerson)
var anotherPerson = new AnotherPerson { Age = 30, Name = "Tony Montana", Kid = new Kid { KidAge = 10, KidName = "SomeName" } };
var properties = typeof(AnotherPerson).GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == typeof(Kid)){
var pp = typeof(Kid).GetProperties();
Console.WriteLine("Kid:");
foreach (PropertyInfo p in pp)
{
Console.WriteLine("\t{0} = {1}", p.Name, p.GetValue(anotherPerson.Kid, null));
}
}
else
Console.WriteLine("{0} = {1}", property.Name, property.GetValue(anotherPerson, null));
}
The code loops through the properties and uses ToString (through the String.Format method that Console.WriteLine uses) to get the string representation of the property values.
If you want to change how the Kid property value is displayed, you basically have two options:
Check the type of the property (property.PropertyType) in the reflection code, so that you can do something different for the Kid type.
Override the ToString method in the Kid class, so that it returns whatever string you want to display as value.
A method doing some recursion can get the values for properties within properties:
public static string GetProperties(object obj)
{
var t = obj.GetType();
var properties = t.GetProperties();
var s = "";
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(obj, new object[0]);
string propertyToString;
if (t.GetMethod("ToString").DeclaringType != typeof(object))
propertyToString = propValue.ToString();
else if (typeof(IConvertible).IsAssignableFrom(property.PropertyType))
propertyToString = Convert.ToString(propValue);
else
propertyToString = GetProperties(propValue);
s += string.Format("'{0}' = '{1}'", property.Name, propertyToString);
}
return s;
}
If you had practical purposes in mind, JSON (or other) serialization may be more appropriate, e.g. using JSON.net would produce a similar string.
I am trying to build an expression for sorting, and i wrote code that sorts my list using one property.
But I need to sort it firstly by one property, secondly by another property and so on.
I mean I want to build an expression that will implement something like that: students.OrderBy(fistExpression.Compile()).ThenBy(secondImpression.Complie()).ThenBy(thirdExpression.Compile()).
So how to dynamically put that ThenBy methods?
Here is my code:
Type studentType = typeof(Student);
ParameterExpression studentParam = Expression.Parameter(studentType, "x");
MemberInfo ageProperty = studentType.GetProperty("Age");
MemberExpression valueInNameProperty =
Expression.MakeMemberAccess(studentParam, ageProperty);
Expression<Func<Student, int>> orderByExpression =
Expression<Func<Student, int>>.Lambda<Func<Student, int>>(valueInNameProperty, studentParam);
var sortedStudents = students.OrderBy(orderByExpression.Compile());
My solution:
public static Func<Student, object> BuildPredicate(string propertyName)
{
Type studentType = typeof(Student);
ParameterExpression studentParam = Expression.Parameter(studentType, "x");
MemberInfo ageProperty = studentType.GetProperty(propertyName);
MemberExpression valueInNameProperty = Expression.MakeMemberAccess(studentParam, ageProperty);
UnaryExpression expression = Expression.Convert(valueInNameProperty, typeof (object));
Expression<Func<Student, object>> orderByExpression = Expression.Lambda<Func<Student, object>>(expression, studentParam);
return orderByExpression.Compile();
}
in your expression making code is added casting to object.
That is how you can create a chain of ThenBy:
var sortedStudents = students.OrderBy(BuildPredicate("Age"));
foreach (var property in typeof(Student).GetProperties().Where(x => !String.Equals(x.Name, "Age")))
{
sortedStudents = sortedStudents.ThenBy(BuildPredicate(property.Name));
}
var result = sortedStudents.ToList();
Finally, Student sample class:
public class Student
{
public int Age { get; set; }
public string Name { get; set; }
}
Update:
Another approach is using attributes to mark properies from your Student to use them in OrderBy and ThenBy. Like:
public class Student
{
[UseInOrderBy]
public int Age { get; set; }
[UseInOrderBy(Order = 1)]
public string Name { get; set; }
}
[AttributeUsage(AttributeTargets.Property)]
internal class UseInOrderByAttribute : Attribute
{
public int Order { get; set; }
}
That is how you can build sorting chain using UseInOrderByAttribute:
Type studentType = typeof (Student);
var properties = studentType.GetProperties()
.Select(x => new { Property = x, OrderAttribute = x.GetCustomAttribute<UseInOrderByAttribute>() })
.Where(x => x.OrderAttribute != null)
.OrderBy(x => x.OrderAttribute.Order);
var orderByProperty = properties.FirstOrDefault(x => x.OrderAttribute.Order == 0);
if (orderByProperty == null)
throw new Exception("");
var sortedStudents = students.OrderBy(BuildPredicate(orderByProperty.Property.Name));
foreach (var property in properties.Where(x => x.Property.Name != orderByProperty.Property.Name))
{
sortedStudents = sortedStudents.ThenBy(BuildPredicate(property.Property.Name));
}
var result = sortedStudents.ToList();
Fix: BuildPredicate can be writen without dynamic. BuildPredicate sample code is changed.
I assume that you have private properties that you want to be able to sort.
If you for example have this class:
public class Student
{
public Student (int age, string name)
{
Age = age;
Name = name;
}
private string Name { get; set; }
public int Age { get; set; }
public override string ToString ()
{
return string.Format ("[Student: Age={0}, Name={1}]", Age, Name);
}
}
You can use the following method to build expressions that will get both public and private properties:
public static Func<TType, TResult> CreateExpression<TType, TResult>(string propertyName)
{
Type type = typeof(TType);
ParameterExpression parameterExpression = Expression.Parameter(type, propertyName);
MemberInfo property = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
MemberExpression valueInProperty = Expression.MakeMemberAccess(parameterExpression, property);
return Expression.Lambda<Func<TType,TResult>>(valueInProperty, parameterExpression).Compile();
}
Example of usage:
var students = new [] {
new Student(20, "Ben"),
new Student(20, "Ceasar"),
new Student(20, "Adam"),
new Student(21, "Adam"),
};
var sortedStudents = students
.OrderBy(CreateExpression<Student, string>("Name"))
.ThenBy(CreateExpression<Student, int>("Age"));
sortedStudents.ToList().ForEach(student => Console.WriteLine(student));
/*
Prints:
[Student: Age=20, Name=Adam]
[Student: Age=21, Name=Adam]
[Student: Age=20, Name=Ben]
[Student: Age=20, Name=Ceasar]
*/
Okay here is the scenario! I have a Person object which has an Address object. Person has a list of Addresses.
Now, I want to iterate through the properties of the Person and when I reach a List I want to create an object of the Address object. How can i do that?
Update:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
private List<Address> _addresses = new List<Address>();
public void AddAddress(Address address)
{
_addresses.Add(address);
address.Person = this;
}
public List<Address> Addresses
{
get { return _addresses; }
set { _addresses = value; }
}
}
var properties = item.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach(var property in properties)
{
var propertyType = property.PropertyType;
if (!propertyType.IsGenericType) continue;
var obj = Activator.CreateInstance(propertyType);
var genericType = obj.GetType().GetGenericTypeDefinition();
Console.WriteLine(obj.GetType().Name);
var type = property.GetType();
}
The above reflection code returns me a List but it is of type List. I want the Generic Type which is address.
Tony, if you have access to Address class then you could simply do this.
var properties = item.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
List<Address> retVal;
foreach (var property in properties)
{
var propertyType = property.PropertyType;
if (!propertyType.IsGenericType) continue;
retVal = property.GetValue(item, new object[] { }) as List<Address>;
if (retVal != null)
break;
}
//now you have your List<Address> in retVal