Found this but unsure how to call so i can go - c#

How do i use this function properly i found it on stack without instructions
i declared it as var db = DeepCopy(absenceEntity);
but how do i access the properties within absenceEntity so I can show the orignal values.
I am trying to implment a copy of values to store before savechanges is called is their an easier way to do this its for audit tracking
public static object DeepCopy(object obj)
{
if (obj == null)
return null;
Type type = obj.GetType();
if (type.IsValueType || type == typeof(string))
{
return obj;
}
else if (type.IsArray)
{
Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
var array = obj as Array;
Array copied = Array.CreateInstance(elementType, array.Length);
for (int i = 0; i < array.Length; i++)
{
copied.SetValue(DeepCopy(array.GetValue(i)), i);
}
return Convert.ChangeType(copied, obj.GetType());
}
else if (type.IsClass)
{
object toret = Activator.CreateInstance(obj.GetType());
FieldInfo[] fields = type.GetFields(BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
object fieldValue = field.GetValue(obj);
if (fieldValue == null)
continue;
field.SetValue(toret, DeepCopy(fieldValue));
}
return toret;
}
else
throw new ArgumentException("Unknown type");
}

Either access the properties on absenceEntity before passing it in or simply
var _absenceEntity = DeepCopy(absenceEntity);
and then
_absenceEntity.(specify propperty here)

Related

Common DebuggerDisplay attribute implementation

I'm trying to implement the common/general DebuggerDisplay attribute to avoid formatting it for every single usage which would simply show every property of the class. That's what I've achieved so far, my extension method looks like this
public static class DebuggerExtension
{
public static string ToDebuggerString(this object #this)
{
var builder = new StringBuilder();
foreach (var property in #this.GetType().GetProperties())
{
if (property != null)
{
var value = property.GetValue(#this, null)?.ToString();
builder.Append($"{property.Name}: {value}, ");
}
}
return builder.ToString();
}
}
and I'm calling it like this and it basically works:
[DebuggerDisplay($"{{this.ToDebuggerString()}}")]
My questions are:
Is there any existing extension to generate the DebuggerDisplay string? The built in VS implementation just adds the method which returns ToString() which doesn't really help
Can I call the method on some compiler-checked way, not as a string? "this." is obviously not available in the attribute arguments. Maybe any expression?
Any other possible solutions?
Have you tried using 'ToDebuggerString()' again instead of 'ToString()' in extension method?
It may interest you. Visual Studio's Natvis Debugging Framework Tutorial
The example below may be helpful to you. (It may be missing!)
public static class DebuggerExtension
{
public static string ToDebuggerDisplay(this object o)
{
if (object.ReferenceEquals(o, null))
return "null";
else if (o is string)
return string.Format("\"{0}\"", (string)o);
else if ((o is DateTime) || (o is TimeSpan))
return string.Format("{{{0}}}", o);
else if (o is System.Collections.ICollection)
return string.Format("{{Count={0}}}", ((System.Collections.ICollection)o).Count);
else if (o is System.Collections.IEnumerable)
{
int nCount = 0;
System.Collections.IEnumerator e = ((System.Collections.IEnumerable)o).GetEnumerator();
while (e.MoveNext())
{
nCount++;
}
return string.Format("{{Count={0}}}", nCount);
}
Type objType = o.GetType();
if (objType.IsPrimitive || objType.IsEnum)
return o.ToString();
else if (objType.IsArray)
return string.Format("{{Count={0}}}", ((Array)o).Length);
PropertyInfo[] propertyInfos = objType.GetProperties(BindingFlags.Instance
| BindingFlags.Public
| BindingFlags.GetProperty
| BindingFlags.DeclaredOnly);
string retVal = "{";
int ndx = 0;
foreach (PropertyInfo pi in propertyInfos)
{
if (!pi.CanRead)
continue;
DebuggerBrowsableAttribute attr = pi.GetCustomAttribute<DebuggerBrowsableAttribute>();
if (!object.ReferenceEquals(attr, null) && (attr.State == DebuggerBrowsableState.Never))
continue;
if (ndx++ > 0)
retVal += " ";
retVal += string.Format("{0}={1}", pi.Name, pi.GetValue(o).ToDebuggerDisplay());
if (ndx > 2)
{
retVal += " ...";
break;
}
}
retVal += "}";
return (ndx > 0) ? retVal : o.ToString();
}
}
Uses
[DebuggerDisplay(#"{this.ToDebuggerDisplay(),nq}")]

Getting fields and properties' names, types and values recursively in C#

I am trying to recursively iterate over one root class to grab it's values and build a tree structure for visualization.
For this I have a class that can be initialized with an object
public Item(object genericObject, int depth = 0) {
this.depth = depth;
this.Name = genericObject.GetType().Name;
this.Type = genericObject.GetType().ToString();
this.Value = genericObject;
this.subItems = new List<Item>();
foreach (MemberInfo member in Item.GetType().GetMembers())
{
if (member.MemberType != MemberTypes.Method && member.MemberType != MemberTypes.Constructor)
subItems.Add(new Item(member,genericObject, depth + 1));
}
}
and another constructor for recursion
public Item(MemberInfo member,object parentobj, int depth = 0)
{
if (member.MemberType == MemberTypes.Property)
{
this.FieldName = ((PropertyInfo)member).Name;
this.FieldType = ((PropertyInfo)member).PropertyType.ToString();
this.fieldValue = ((PropertyInfo)member).GetValue(parentobj);
}
else
{
this.FieldName = ((FieldInfo)member).Name;
this.FieldType = ((FieldInfo)member).GetValue(parentobj).GetType().Name;
this.fieldValue = ((FieldInfo)member).GetValue(parentobj);
}
switch (member)
{
case PropertyInfo propertyInfo:
case FieldInfo fieldinfo:
bool found = false;
foreach (string typename in browsethroughtheseTypes)
{
if (this.FieldType.ToLower().Contains(typename)) found = true;
}
if (found)
{
this.subItems = new List<Displayable>();
foreach (MemberInfo mem in member.GetType().GetMembers())
{
if (mem.GetType().IsClass)
this.subItems.Add(new Item(mem, depth + 1));
}
}
break;
default:
break;
}
}
For testing I only added list to browsethroughthesetypes
After recursion takes place in the second constructor all I'm seeing is the internal types.
Is there a proper way to achieve this using reflection?

How can you translate a flat dotted list of properties to a constructed object?

I have a list of properties and their values and they are formatted in a Dictionary<string, object> like this:
Person.Name = "John Doe"
Person.Age = 27
Person.Address.House = "123"
Person.Address.Street = "Fake Street"
Person.Address.City = "Nowhere"
Person.Address.State = "NH"
There are two classes. Person is composed of the string Name and primitive Age as well as the complex Address class which has House, Street, City, and State string properties.
Basically what I want to do is look up the class Person in the current assembly and create an instance of it and assign all of the values, no matter how complicated the classes get, as long as at the deepest level they are composed of primitives, strings, and a few common structures such as DateTime.
I have a solution which allows me to assign the top level properties and down into one of the complex properties. I am assuming I must use recursion to solve this, but I would prefer not to.
Though, even with recursion, I am at a loss as to a nice way to get down into each of the properties and assign their values.
In this example below I am trying to translate the dotted representation to classes based on a method's parameters. I look up the appropriate dotted representation based on the parameter's type, trying to find a match. DotField is basically a KeyValuePair<string, object> where the key is the Name property. The code below may not work correctly but it should express the idea well enough.
foreach (ParameterInfo parameter in this.method.Parameters)
{
Type parameterType = parameter.ParameterType;
object parameterInstance = Activator.CreateInstance(parameterType);
PropertyInfo[] properties = parameterType.GetProperties();
foreach (PropertyInfo property in properties)
{
Type propertyType = property.PropertyType;
if (propertyType.IsPrimitive || propertyType == typeof(string))
{
string propertyPath = String.Format("{0}.{1}", parameterType.Name, propertyType.Name);
foreach (DotField df in this.DotFields)
{
if (df.Name == propertyPath)
{
property.SetValue(parameterInstance, df.Value, null);
break;
}
}
}
else
{
// Somehow dive into the class, since it's a non-primitive
}
}
}
Your Dictionary sounds similar to JSON-formatted data. If you first transform it into a compatible form, you can use Json.Net to convert the dictionary into your object. Here's an example of that:
public static void Main()
{
var dict = new Dictionary<string, object>
{
{"Person.Name", "John Doe"},
{"Person.Age", 27},
{"Person.Address.House", "123"},
{"Person.Address.Street", "Fake Street"},
{"Person.Address.City", "Nowhere"},
{"Person.Address.State", "NH"},
};
var hierarchicalDict = GetItemAndChildren(dict, "Person");
string json = JsonConvert.SerializeObject(hierarchicalDict);
Person person = JsonConvert.DeserializeObject<Person>(json);
// person has all of the values you'd expect
}
static object GetItemAndChildren(Dictionary<string, object> dict, string prefix = "")
{
object val;
if (dict.TryGetValue(prefix, out val))
return val;
else
{
if (!string.IsNullOrEmpty(prefix))
prefix += ".";
var children = new Dictionary<string, object>();
foreach (var child in dict.Where(x => x.Key.StartsWith(prefix)).Select(x => x.Key.Substring(prefix.Length).Split(new[] { '.' }, 2)[0]).Distinct())
{
children[child] = GetItemAndChildren(dict, prefix + child);
}
return children;
}
}
You could also use reflection for doing so. I had fun writing this down :)
private object Eval(KeyValuePair<string, object> df)
{
var properties = df.Key.Split('.');
//line below just creates the root object (Person), you could replace it with whatever works in your example
object root = Activator.CreateInstance(Assembly.GetExecutingAssembly().GetTypes().First(t => t.Name == properties.First()));
var temp = root;
for (int i = 1; i < properties.Length - 1; i++)
{
var propertyInfo = temp.GetType().GetProperty(properties[i]);
var propertyInstance = Activator.CreateInstance(propertyInfo.PropertyType);
propertyInfo.SetValue(temp, propertyInstance, null);
temp = propertyInstance;
}
temp.GetType().GetProperty(properties.Last()).SetValue(temp, df.Value, null);
return root;
}
Here is my complete code example. I decided to stay away from the crazy amount of reflection and mapping and get a lot of context information from the dotted list structure.
I want to thank Tim and rla4 for their solutions and providing the information that brought me to this solution.
private static int GetPathDepth(string path)
{
int depth = 0;
for (int i = 0; i < path.Length; i++)
{
if (path[i] == '.')
{
depth++;
}
}
return depth;
}
private static string GetPathAtDepth(string path, int depth)
{
StringBuilder pathBuilder = new StringBuilder();
string[] pathParts = path.Split('.');
for (int i = 0; i < depth && i < pathParts.Length; i++)
{
string pathPart = pathParts[i];
if (i == depth - 1 || i == pathParts.Length - 1)
{
pathBuilder.Append(pathPart);
}
else
{
pathBuilder.AppendFormat("{0}.", pathPart);
}
}
string pathAtDepth = pathBuilder.ToString();
return pathAtDepth;
}
private static string[] GetIntermediatePaths(string path)
{
int depth = GetPathDepth(path);
string[] intermediatePaths = new string[depth];
for (int i = 0; i < intermediatePaths.Length; i++)
{
string intermediatePath = GetPathAtDepth(path, i + 1);
intermediatePaths[i] = intermediatePath;
}
return intermediatePaths;
}
private static PropertyInfo GetProperty(Type root, string path)
{
PropertyInfo result = null;
string[] pathParts = path.Split('.');
foreach (string pathPart in pathParts)
{
if (Object.ReferenceEquals(result, null))
{
result = root.GetProperty(pathPart);
}
else
{
result = result.PropertyType.GetProperty(pathPart);
}
}
if (Object.ReferenceEquals(result, null))
{
throw new ArgumentException("A property at the specified path could not be located.", "path");
}
return result;
}
private static object GetParameter(ParameterInfo parameter, Dictionary<string, string> valueMap)
{
Type root = parameter.ParameterType;
Dictionary<string, object> instanceMap = new Dictionary<string, object>();
foreach (KeyValuePair<string, string> valueMapEntry in valueMap)
{
string path = valueMapEntry.Key;
string value = valueMapEntry.Value;
string[] intermediatePaths = GetIntermediatePaths(path);
foreach (string intermediatePath in intermediatePaths)
{
PropertyInfo intermediateProperty = GetProperty(root, intermediatePath);
object propertyTypeInstance;
if (!instanceMap.TryGetValue(intermediatePath, out propertyTypeInstance))
{
propertyTypeInstance = Activator.CreateInstance(intermediateProperty.PropertyType);
instanceMap.Add(intermediatePath, propertyTypeInstance);
}
}
PropertyInfo property = GetProperty(root, path);
TypeConverter converter = TypeDescriptor.GetConverter(property.PropertyType);
object convertedValue = converter.ConvertFrom(value);
instanceMap.Add(path, convertedValue);
}
object rootInstance = Activator.CreateInstance(root);
foreach (KeyValuePair<string, object> instanceMapEntry in instanceMap)
{
string path = instanceMapEntry.Key;
object value = instanceMapEntry.Value;
PropertyInfo property = GetProperty(root, path);
object instance;
int depth = GetPathDepth(path);
if (depth == 0)
{
instance = rootInstance;
}
else
{
string parentPath = GetPathAtDepth(path, depth);
instance = instanceMap[parentPath];
}
property.SetValue(instance, value);
}
return rootInstance;
}

How can I create an object with a set property from a string Property path like data binding does?

I have a list of property paths and values as strings in a text file. Is there a mapping tool or serialize that could take a property path string( "Package.Customer.Name" ) and create the object and set the value?
/// <summary>
/// With a data row, create an object and populate it with data
/// </summary>
private object CreateObject(List<DataCell> pRow, string objectName)
{
Type type = Type.GetType(objectName);
if (type == null)
throw new Exception(String.Format("Could not create type {0}", objectName));
object obj = Activator.CreateInstance(type);
foreach (DataCell cell in pRow)
{
propagateToProperty(obj, *propertypath*, cell.CellValue);
}
return obj;
}
public static void propagateToProperty(object pObject, string pPropertyPath, object pValue)
{
Type t = pObject.GetType();
object currentO = pObject;
string[] path = pPropertyPath.Split('.');
for (byte i = 0; i < path.Length; i++)
{
//go through object hierarchy
PropertyInfo pi = t.GetProperty(path[i]);
if (pi == null)
throw new Exception(String.Format("No property {0} on type {1}", path[i], pObject.GetType()));
//an object in the property path
if (i != path.Length - 1)
{
t = pi.PropertyType;
object childObject = pi.GetValue(currentO, null);
if (childObject == null)
{
childObject = Activator.CreateInstance(t);
pi.SetValue(currentO, childObject, null);
}
currentO = childObject;
//the target property
else
{
if (pi.PropertyType.IsEnum && pValue.GetType() != pi.PropertyType)
pValue = Enum.Parse(pi.PropertyType, pValue.ToString());
if (pi.PropertyType == typeof(Guid) && pValue.GetType() != pi.PropertyType)
pValue = new Guid(pValue.ToString());
if (pi.PropertyType == typeof(char))
if (pValue.ToString().Length == 0)
pValue = ' ';
else
pValue = pValue.ToString()[0];
pi.SetValue(currentO, pValue, null);
}
}
}
Thats what I use. I just had it open in VS, maybe it can help you. The propertyPath is a placeholder for your property path, i cut that out from my code.
If you re looking for a more robust solution, Automapper would probably work nicely, as suggested by someone else already.

Setting a string property via reflection removes leading zeros if the value can be casted as number

I have a framework that handles comunication with the database. It calls my SPs (search, ins, del, upd) filling the parameters automatically by matching an object's property name with the parameter's name. Everything was working great until I got a string value that contains only numbers, and the first number is zero. The code is automatically removing leading zeros, even though both my property and the DB column are string.
Here is a portion of my code:
var property = from p in entityProperties
where p.Name.ToUpper().Equals(dvSchema[index][0].ToString().ToUpper())
select p;
object propertyNewValue;
if (property.First().PropertyType.BaseType != typeof(Enum))
propertyNewValue = dr[index];
...
property.First().SetValue(businessEntity, propertyNewValue, null);
I'm using .NET 3.5 and SQL Server2008
Does anyone know how to avoid this, hopefully other than adding an if to check if the property type is string?
Thanks in advance!
EDIT
Here's my full procedure, I don't explicitly cast the property anywhere:
public virtual void MapEntityFromDataBase(BE businessEntity, IDataReader dr)
{
DataView dvSchema = dr.GetSchemaTable().DefaultView;
Type thisType = businessEntity.GetType();
while (thisType != typeof(Object))
{
PropertyInfo[] entityProperties = thisType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
for (int index = 0; index < dvSchema.Count; index++)
{
if (dr[index] != DBNull.Value)
{
var property = from p in entityProperties
where p.Name.ToUpper().Equals(dvSchema[index][0].ToString().ToUpper())
select p;
if (property.Count() > 0)
{
object propertyNewValue;
if (property.First().PropertyType.BaseType != typeof(Enum))
{
propertyNewValue = dr[index];
}
else //enums throw a cast exception
{
if (dr[index].GetType() == typeof(string) || dr[index].GetType() == typeof(char))
propertyNewValue = Enum.Parse(property.First().PropertyType, ((int)dr[index].ToString()[0]).ToString());
else
propertyNewValue = Enum.Parse(property.First().PropertyType, dr[index].ToString());
}
if (property.First().CanWrite)
{
if (property.First().PropertyType.IsGenericType && property.First().PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
Type[] typeCol = property.First().PropertyType.GetGenericArguments();
Type nullableType;
if (typeCol.Length > 0)
{
nullableType = typeCol[0];
if (nullableType.BaseType == typeof(Enum))
{
propertyNewValue = Enum.Parse(nullableType, propertyNewValue.ToString());
}
}
}
property.First().SetValue(businessEntity, propertyNewValue, null);
}
}
}
}
thisType = thisType.BaseType;
}
}
The strange thing is that when I get to the property setting sentence
property.First().SetValue(businessEntity, propertyNewValue, null);
propertyNewValue has a value of "091234" (as seen by debbuger)

Categories

Resources