Logging state of object. Getting all its property values as string - c#

public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
......
var emp1Address = new Address();
emp1Address.AddressLine1 = "Microsoft Corporation";
emp1Address.AddressLine2 = "One Microsoft Way";
emp1Address.City = "Redmond";
emp1Address.State = "WA";
emp1Address.Zip = "98052-6399";
Consider above class and later its initialization. Now at some point I want to log its state when error occurs. I would like to get the string log somewhat like below.
string toLog = Helper.GetLogFor(emp1Address);
sting toLog should look something like below.
AddressLine1 = "Microsoft Corporation";
AddressLine2 = "One Microsoft Way";
City = "Redmond";
State = "WA";
Zip = "98052-6399";
And then I will log toLog string.
How can I access all the property names and property values of an object within Helper.GetLogFor() method?
Solution that I implemented:-
/// <summary>
/// Creates a string of all property value pair in the provided object instance
/// </summary>
/// <param name="objectToGetStateOf"></param>
/// <exception cref="ArgumentException"></exception>
/// <returns></returns>
public static string GetLogFor(object objectToGetStateOf)
{
if (objectToGetStateOf == null)
{
const string PARAMETER_NAME = "objectToGetStateOf";
throw new ArgumentException(string.Format("Parameter {0} cannot be null", PARAMETER_NAME), PARAMETER_NAME);
}
var builder = new StringBuilder();
foreach (var property in objectToGetStateOf.GetType().GetProperties())
{
object value = property.GetValue(objectToGetStateOf, null);
builder.Append(property.Name)
.Append(" = ")
.Append((value ?? "null"))
.AppendLine();
}
return builder.ToString();
}

public static string GetLogFor(object target)
{
var properties =
from property in target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
select new
{
Name = property.Name,
Value = property.GetValue(target, null)
};
var builder = new StringBuilder();
foreach(var property in properties)
{
builder
.Append(property.Name)
.Append(" = ")
.Append(property.Value)
.AppendLine();
}
return builder.ToString();
}

static void Log(object #object)
{
foreach (var property in #object.GetType().GetProperties())
Console.WriteLine(property.Name + ": " + property.GetValue(#object, null).ToString());
}

You can access the property name using reflection
like following
Type t = emp1Address.GetType();
PropertyInfo [] pi = t.GetProperties();
foreach (PropertyInfo p in pi)
{
//You can get the value (using GetValue() method) and name (p.Name) here.
}

Related

Reflection to get List<object> data

I'm trying to loop through a DetailClass objects inside a List using reflection just like for string fields, but I can't figure out how.
class DetailClass
{
public string FieldDetail1 { get; set; }
public string FieldDetail2 { get; set; }
public string FieldDetail3 { get; set; }
}
class SomeClass
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public List<DetailClass> Artikli { get; set; }
}
private static PropertyInfo[] GetProperties(object obj)
{
return obj.GetType().GetProperties();
}
var myData = new SomeClass();
var prop = GetProperties(myData);
foreach (var item in prop)
{
if (item.PropertyType == typeof(string))
{
var name = item.Name,
var value = item.GetValue(myData).ToString()));
}
//how to get name and value for data inside List<DetailClass>?
}
You were trying to enumerate properties of the parent class
GetValue needs the a reference to the class you are dealing with
Code
var myData = new SomeClass();
myData.Artikli = new List<DetailClass>() { new DetailClass() { FieldDetail1 = "asd", FieldDetail2 = "sdfd", FieldDetail3 = "sdfsg" } };
foreach (var obj in myData.Artikli)
{
foreach (var item in obj.GetType().GetProperties())
{
if (item.PropertyType == typeof(string))
{
var name = item.Name;
var val = item.GetValue(obj);
Console.WriteLine(name + ", " + val);
}
}
}
Demo Here
Additional Resources
PropertyInfo.GetValue Method (Object)
Returns the property value of a specified object.
Parameters
obj
Type: System.Object
The object whose property value will be returned.
You can use your method recursively to get inside all layer of properties
You can check if
item.PropertyType.GetInterfaces().Contains(typeof(IEnumerable))
and if true cast (IEnumerable)item.GetValue(myData) and iterate on the result
recursively.
Just like TheDude answered, you can use a recursive method like so;
private void Recursion(object obj)
{
var props = GetProperties(obj);
foreach (var item in props)
{
if (item.PropertyType == typeof(string))
{
var name = item.Name;
var value = item.GetValue(obj)?.ToString();
}
else if (item.PropertyType == typeof(List<DetailClass>))
{
var test = (List<DetailClass>) item.GetValue(obj);
foreach (var t in test)
{
Recursion(t);
}
}
}
}
And do whatever you want with the name and values in the list.

Enum Extension method call by Enum Name?

I have following code which is working fine . but i need to use extension method with its Enum type.
public class Dropdown
{
public Dropdown() { }
public Dropdown(int id, string name)
{
Id = id;
Name = name;
}
public Dropdown(string name)
{
Name = name;
}
public int Id { get; set; }
public string StringId { get; set; }
public string Name { get; set; }
}
public enum AccidentTypeEnum
{
[Display(Name = "Minor")]
Minor = 0,
[Display(Name = "Major")]
Major = 1,
[Display(Name = "Severe")]
Severe = 2
}
Extension Method
public static class EnumExtensions
{
public static List<Dropdown> ConvertToDropdown(this Enum mEnum)
{
var dropDownlist = new List<Dropdown>();
var enumType = mEnum.GetType();
var enumValuies = Enum.GetValues(enumType);
foreach (var singleValue in enumValuies)
{
dropDownlist.Add(new Dropdown { Id = (int)singleValue, Name = singleValue.ToString() });
}
return dropDownlist;
}
}
Question:
Above code is working fine if i call extension method using below lines
var TestAcident = AccidentTypeEnum.Major;
var resultDropdown = TestAcident.ConvertToDropdown();
But how can i call it just like below lines
var resultDropdown = AccidentTypeEnum.ConvertToDropdown(); //<-- i need to use like this. but it not working
You can do that
List<AccidentTypeEnum> types = Utils<AccidentTypeEnum>.ConvertToDropdown();
this way:
/// <summary>Pseudo extension class for enumerations</summary>
/// <typeparam name="TEnum">Enumeration type</typeparam>
public class Utils<TEnum> where TEnum : struct, IConvertible
{
public static List<Dropdown> ConvertToDropdown()
{
var enumType = typeof(TEnum);
return enumType.IsEnum
? enumType.GetEnumValues()
.OfType<TEnum>()
.Select(e => new Dropdown
{
Id = Convert.ToInt32(Enum.Parse(enumType, e.ToString()) as Enum),
Name = GetDisplay(e)
})
.ToList()
: throw new ArgumentException($"{enumType.Name} is not enum");
}
private static string GetDisplay<T>(T value)
{
var enumValueText = value.ToString();
var displayAttribute = value
.GetType()
.GetField(enumValueText)
.GetCustomAttributes(typeof(DisplayAttribute), false)
.OfType<DisplayAttribute>()
.FirstOrDefault();
return displayAttribute == null ? enumValueText : displayAttribute.Description;
}
}

Write an Observable Collection to Csv file

I have a datagridview that is bound to an observable collection in a mvvm fashion.
I'm trying to figure out how to write the collection to a csv file.
I can format the headers and get that put in, but not sure how one would iterate over a collection pulling out the values and putting them to a file with comma delimiting.
Here is my class
public class ResultsModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Phone { get; set; }
public string Username { get; set; }
public bool Sucess { get; set; }
public string MessageType { get; set; }
public string SenderMessageSent { get; set; }
public string SenderMessageReceived { get; set; }
}
which gets loaded into an observable collection
Here's a generic helper method which utilizes reflection to get values of all properties in the collection of objects and serializes to comma separated values string. (1 line = 1 object from collection)
public static IEnumerable<string> ToCsv<T>(IEnumerable<T> list)
{
var fields = typeof(T).GetFields();
var properties = typeof(T).GetProperties();
foreach (var #object in list)
{
yield return string.Join(",",
fields.Select(x => (x.GetValue(#object) ?? string.Empty).ToString())
.Concat(properties.Select(p => (p.GetValue(#object, null) ?? string.Empty).ToString()))
.ToArray());
}
}
And the examplary usage:
var oemResultsModels = new List<OemResultsModel>
{
new OemResultsModel
{
FirstName = "Fname1",
LastName = "LName1",
MessageType = "Type1",
Phone = 1234567,
SenderMessageReceived = "something1",
SenderMessageSent = "somethingelse1",
Sucess = true,
Username = "username1"
},
new OemResultsModel
{
FirstName = "Fname2",
LastName = "LName2",
MessageType = "Type2",
Phone = 123456789,
SenderMessageReceived = "something2",
SenderMessageSent = "somethingelse2",
Sucess = false,
Username = "username2"
}
};
using (var textWriter = File.CreateText(#"C:\destinationfile.csv"))
{
foreach (var line in ToCsv(oemResultsModels))
{
textWriter.WriteLine(line);
}
}

Getting description of Enum and using this as text property of dropdownlist in MVC?

I have a dropdown control inside my view. Now i want it to have Description of enum as it's text. I am least worried about Id but i need text to be well formatted.
Refer the following code.
public static List<EnumModel> GetEnumList<T>()
{
var enumValues = Enum.GetValues(typeof(T)).Cast<T>().Select(rentalType => new EnumModel()
{
Value = Convert.ToInt32(rentalType),
Name = GetDisplayName<T>(rentalType, false)
}).ToList();
return enumValues;
}
public static string GetDisplayName<T>(T value, bool isDisplayName)
{
if (value == null)
{
throw new ArgumentNullException("value", "Enum value Empty");
}
var type = value.GetType();
var field = type.GetField(value.ToString());
if (field == null)
{
return value.ToString();
}
var attributes = ((DisplayAttribute[])field.GetCustomAttributes(typeof(DisplayAttribute), false)).FirstOrDefault();
return attributes != null ? isDisplayName == true ? attributes.GetDescription() : attributes.Description : value.ToString();
}
public class EnumModel
{
/// <summary>
/// Gets or sets the value
/// </summary>
public int Value { get; set; }
/// <summary>
/// Gets or sets the name
/// </summary>
public string Name { get; set; }
}
You can get List<EnumModel> as ENUM list with name and value.What you need to do is just make a List<SelectListitem> from List<EnumModel>
Hope this helps.
Use this code and bind with your dropdown-
public static List<SelectListItem> GetSelectList(Type enumType, String SelectedValue, Boolean IsValString = true)
{
Array values = Enum.GetValues(enumType);
List<SelectListItem> selectListItems = new List<SelectListItem>(values.Length);
foreach (var i in Enum.GetValues(enumType))
{
String name = Enum.GetName(enumType, i);
String desc = name;
FieldInfo fi = enumType.GetField(name);
var attributes = fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
String result = attributes.Length == 0 ? desc : ((DescriptionAttribute)attributes[0]).Description;
var selectItem = new SelectListItem()
{
Text = result,
Value = (IsValString) ? i.ToString() : ((Int32)i).ToString()
};
if ((SelectedValue != null) && (SelectedValue.Equals(selectItem.Value)))
{
selectItem.Selected = true;
}
selectListItems.Add(selectItem);
}
return selectListItems;
}

.Net Get Property Values of Object by Key, to any depth

I would like to be able to access the value of an object property to any depth having only the string-key of the property. Also, if possible, using collection indexing on List properties.
So, If I have the string "Person.Surname" then I could get the value "Smith" from and instanciated CaseConductor object. So given some setup code like this ...
//- Load a caseConductor
var caseConductor = new CaseConductor();
caseConductor.CaseID = "A00001";
// person
caseConductor.Person = new Person();
caseConductor.Person.Surname = "Smith" ;
caseConductor.Person.DOB = DateTime.Now ;
// case note list
caseConductor.CaseNoteList = new List<Note>();
caseConductor.CaseNoteList.Add(new Note { NoteText = "A-1" , NoteDt = DateTime.Now });
caseConductor.CaseNoteList.Add(new Note { NoteText = "B-2", NoteDt = DateTime.Now });
// I could do this ...
object val = caseConductor.SomeCleverFunction("Person.Surname");
// or this ...
object val = caseConductor.SomeCleverFunction("CaseNoteList[0].NoteText");
Has anyone done this before ?
Here are some setup classes ...
class Note
{
public Guid NoteID { get; set; }
public string NoteText { get; set; }
public DateTime? NoteDt { get; set; }
}
public class Person
{
public Guid PersonID { get; set; }
public string Surname { get; set; }
public string Forename { get; set; }
public DateTime? DOB { get; set; }
}
class CaseConductor
{
public String CaseID{get;set;}
public Person Person { get; set; }
public List<Note> CaseNoteList { get; set; }
}
Our use case is to iterate over a series of appropriately named content controls in a word dcoument template using open xml sdk 2, and poke values into a newly created word documents, something like this ...
List<SdtElement> ccList = wordprocessingDocument.MainDocumentPart.Document.Descendants<SdtElement>().ToList();
foreach (var cc in ccList)
{
string alias = cc.SdtProperties.GetFirstChild<SdtAlias>().Val.Value;
switch (cc.GetType().Name)
{
case "SdtRun":
SdtRun thisRun = (SdtRun)cc;
//thisRun.Descendants<Text>().First().Text = theValueToBePoked ;
break;
}
}
Use good old reflection. I have tested and this actually works:
public static object GetValue(object o, string propertyName)
{
Type type = o.GetType();
PropertyInfo propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance ).Where(x => x.Name == propertyName).FirstOrDefault();
if(propertyInfo!=null)
{
return propertyInfo.GetValue(o, BindingFlags.Instance, null, null, null);
}
else
{
return null; // or throw exception
}
}
I'm assuming that
caseConductor.SomeCleverFunction
is not a static method, and has access to the Person object, and that the Person object itself if a public property.
I'm also assuming that you want to pass a string like "prop.address.street" where each sub property is an class that containts a puplic property with that name
Split the string input on the period, find the left most string
Use reflection to get a list of properties ( typeof(caseconductor).GetProperties() )
Find the matching property, call GetValue on it, passing the last known solid object (starting with 'this') and storing a refernce to it.
if there is more sub properties in the string left, repeat to step 1, removing the left most part of the string.
otherwise, call GetValue() on the property, using the last GetValue() return object from step 3, and return it.
Something like:
"prop.address.street" -> find property "prop" from 'this' and GetValue,
there is still more "."'s so repeat, storing return value
"address.street" -> find property "address" from the last returned GetValue, and get it's value.
there is still more "."'s so repeat, storing return value
"street" -> find property "street" from the last returned GetValue, and return it's value.
End of string, return last value
Edit -
This is pretty rough, but toss it into LinqPAD and take a look.
http://www.linqpad.net/
Edit #2 - you should be able to index into arrays using the ^ syntax below.
Again this is reaaaaaaaaally rough, just enough to get a working example.
Edit #3 - Cleaned up the example slightly and changed it from my example classes to yours.
void Main()
{
//- Load a caseConductor
var caseConductor = new CaseConductor();
caseConductor.CaseID = "A00001";
// person
caseConductor.Person = new Person();
caseConductor.Person.Surname = "Smith" ;
caseConductor.Person.DOB = DateTime.Now ;
// case note list
caseConductor.CaseNoteList = new List<Note>();
caseConductor.CaseNoteList.Add(new Note { NoteText = "A-1" , NoteDt = DateTime.Now });
caseConductor.CaseNoteList.Add(new Note { NoteText = "B-2", NoteDt = DateTime.Now });
// I could do this ...
string val1 = caseConductor.GetPropertyValue<string>("Person.Surname");
// or this ...
Note val2 = caseConductor.GetPropertyValue<Note>("CaseNoteList^1");
val1.Dump("val1"); //this is a string
val2.Dump("val2"); //this is a Note
}
public static class extensions
{
public static T GetPropertyValue<T>(this object o,string Properties) where T:class
{
var properties = Properties.Split('.');
var indexsplit = properties[0].Split('^');
var current = indexsplit[0];
var prop = (from p in o.GetType().GetProperties() where p.Name == current select p).Take(1).Single();
var val = prop.GetValue(o,null);
if(indexsplit.Length>1)
{
var index = int.Parse(indexsplit[1]);
IList ival = (IList)val;
val = ival[index];
}
if(properties[0] == Properties)
return (T)val;
else
return val.GetPropertyValue<T>(Properties.Replace(properties[0]+".",""));
}
}
class Note
{
public Guid NoteID { get; set; }
public string NoteText { get; set; }
public DateTime? NoteDt { get; set; }
}
public class Person
{
public Guid PersonID { get; set; }
public string Surname { get; set; }
public string Forename { get; set; }
public DateTime? DOB { get; set; }
}
class CaseConductor
{
public String CaseID{get;set;}
public Person Person { get; set; }
public List<Note> CaseNoteList { get; set; }
}
OK, I came up with something which continues Aliosted and asowyer start suggestions, here it is. You can see I still having trouble with the index access of composed objects. Thnaks for your help.
#region object data ...
var model = new HcmlDocumentProductionModel();
model.CaseID = "A001";
model.CaseConductor = new CaseConductor();
model.CaseConductor.AField = "AField";
model.CaseConductor.Person = new Person();
model.CaseConductor.Person.Surname = "{Smith}";
model.CaseConductor.Person.DOB = DateTime.Now;
model.CaseConductor.CaseNoteList = new List<Note>();
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "A-1", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.CaseNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "B-2", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.ReferralNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "C-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.StatusNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "d-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.CaseNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "e-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.StatusNote });
model.CaseConductor.CaseNoteList.Add(new Note { NoteText = "f-3", NoteDt = DateTime.Now, NoteTypeEnum = NoteTypeEnum.CaseNote });
#endregion
string head = "";
string tail = "";
// tail
tail = "".Tail();
tail = "Surname".Tail();
tail = "Person.Surname".Tail();
tail = "CaseConductor.Person.Surname".Tail();
// head
head = "".Head();
head = "Surname".Head();
head = "Person.Surname".Head();
head = "CaseConductor.Person.Surname".Head();
// ObjectDictionary
//var person = new Person { Surname = "Smith" };
//var d = person.ObjectDictionary();
//object ovalue = d["Surname"];
// get value special
object o2 = model.CaseConductor.Person.ValueByKey("Surname");
object o3 = model.CaseConductor.Person.ValueByKey("DOB");
object o4 = model.CaseConductor.ValueByKey("Person.Surname");
object o5 = model.ValueByKey("CaseConductor.Person.Surname");
// get the list of ...
object o6 = model.ValueByKey("CaseConductor.CaseNoteList");
// get item - index thing does not work - get anull here
string noteText = model.CaseConductor.CaseNoteList[1].NoteText;
object o7 = model.ValueByKey("CaseConductor.CaseNoteList[1].NoteText");
namespace Zed
{
public static class Zed
{
public static object ValueByKey(this object o, string key)
{
if (!String.IsNullOrEmpty(key))
{
if (!key.Contains("."))
{
return (o.ObjectDictionary())[key];
}
else
{
// key contains a dot ; therefore get object by the name of the head
// and pass on that object and get propety by the tail
var d = o.ObjectDictionary();
var head = key.Head();
if (head.Contains("["))
{
string headMinusIndexer = head.Substring(0, head.IndexOf("["));
string indexString = head.Between("[", "]");
int index = Convert.ToInt32(indexString);
object oArray = d[headMinusIndexer];
//List<object> oList= d[headMinusIndexer];
// now get the object with the index, ... and continue
//object el = ((object[])oArray)[index];
return null;
}
else
{
var onext = d[head];
return onext.ValueByKey(key.Tail());
}
}
}
return null;
}
public static Dictionary<string,object> ObjectDictionary(this object o)
{
return o.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(o, null));
}
public static string Head(this string key)
{
var head = String.Empty;
var splittBy = '.';
if (!String.IsNullOrEmpty(key))
{
var keyArray = key.Split(splittBy);
head = keyArray[0];
}
//-Return
return head;
}
public static string Tail(this string key)
{
var tail = "";
var splittBy = '.';
if (!String.IsNullOrEmpty(key))
{
var keyArray = key.Split(splittBy);
for (int i = 1; i < keyArray.Length; i++)
{
tail += (i > 1) ? "." + keyArray[i] : keyArray[i];
}
}
//-Return
return tail;
}
public static string Between(this string head, string start, string end)
{
string between = String.Empty ;
between = head.Substring(head.IndexOf(start) + 1, head.IndexOf(end) - head.IndexOf(start) - 1);
return between;
}
public static object ZGetValue( this object o, string propertyName)
{
Type type = o.GetType();
PropertyInfo propertyInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.Name == propertyName).FirstOrDefault();
if (propertyInfo != null)
{
return propertyInfo.GetValue(o, BindingFlags.Instance, null, null, null);
}
else
{
return null;
}
}
}
}

Categories