C# retrieve properties through reflection [duplicate] - c#

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Get property value from string using reflection in C#
I have an application that merges fields from a database into emails and letters. As there are different users they request different fields to be merged, and as I create a new mergefield the documentation also has to be reworked. This gives problems so I want to automate the documentation.
I came up with the following code (in this example I defined only 2 mergefields, called stringField, but currently it's allmost 100):
namespace ReflectionTest
{
public class clReflection
{
private System.Data.DataTable dtClient = new System.Data.DataTable();
public int ClientNumber { get; set; }
public int AdressNumber { get; set; }
public stringField ClientName
{
get
{
stringField _ClientName = new stringField();
_ClientName.FieldContent = "Obama";
_ClientName.FieldNote = "Last name of client";
//Available email and available letter should be default true
return _ClientName;
}
set { }
}
public stringField ClientEmail
{
get
{
stringField _ClientEmail = new stringField();
_ClientEmail.FieldContent = "obama#whitehouse.gov";
_ClientEmail.FieldNote = "use only tested email adresses";
_ClientEmail.AvailableLetter = false;
return _ClientEmail;
}
set
{ }
}
}
public static class FindStringFields
{
public static System.Data.DataTable stringFields()
{
System.Data.DataTable dtStringFields = new System.Data.DataTable();
dtStringFields.Columns.Add(new System.Data.DataColumn("FieldName", typeof(string)));
dtStringFields.Columns.Add(new System.Data.DataColumn("FieldNote", typeof(string)));
dtStringFields.Columns.Add(new System.Data.DataColumn("AvailableEmail", typeof(bool)));
clReflection test = new clReflection();
System.Reflection.PropertyInfo[] props = test.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
for (int i = 0; i < props.Length; i++)
{
if (props[i].PropertyType == typeof(stringField))
{
dtStringFields.Rows.Add(new object[] { props[i].Name , "FieldNote", true });
}
}
return dtStringFields;
}
}
public class stringField
{
private bool _AvailableEmail = true;
private bool _AvailableLetter = true;
public string FieldContent { get; set; }
public string FieldNote { get; set; }
public bool AvailableEmail
{
get { return _AvailableEmail; }
set { AvailableEmail = value; }
}
public bool AvailableLetter
{
get { return _AvailableLetter; }
set { _AvailableLetter = value; }
}
}
}
If you fire the instruction: System.Data.DataTable dtTest = FindStringFields.stringFields();
you get a datatable with all defined stringFields.
However, apart from the name of the stringfield I can't retrieve the properties of a stringfield.
How do I instantiate a found stringField so I can retrieve the other properties?
Thanks,
Rob

Use Type.GetProperties method to get all public properties of type
Type type = typeof(stringField);
PropertyInfo[] properties = type.GetProperties();
Also there is overload where you can specify BindingFlags.
Then you can get values of properties:
object value = property.GetValue(_ClientEmail);

If you have the string field name try with this:
public static object GetPropValue( object src, string propName )
{
return src.GetType( ).GetProperty( propName ).GetValue( src, null );
}
Extracted from this SO post:
Get property value from string using reflection in C#

You can do something like this:
//its better to use a property than get it from the array every time!
PropertyInfo pi = prop[i];
//get the underlying value
stringField underlyingStringField = prop.GetValue(test, null) as stringField;
//use the underlying value now
Debug.Write(underlyingStringField.AvalilableEmail);
...

Related

Class containing a List of a generically Typed Class

I have a generic class, JSTable, that takes a type RowType. I want to have a class that can contain many JSTables, each with a different RowType, so that I may do something like the Func<> in C# does, where it has many optional types. Is this only possible because there are many representations of Func<>? I want a limitless option, so that I could potentially declare a JSGridVM with hundreds of tables, or one table.
public class JSGridVM<?>//where ? is as many types as I want
{
public List<JSTable<?>> Tables { get; set; };
}
public class JSTable<RowType>
{
public JSTable() { }
public JSTable(List<RowType> rows, List<JSGridColumn> columns, bool allowEditingRows, bool allowDeletingRows, string updateURL, string deleteURL)
{
Rows = rows;
Columns = columns;
AllowEditingRows = allowEditingRows;
AllowDeletingRows = allowDeletingRows;
UpdateURL = updateURL;
DeleteURL = deleteURL;
}
public List<RowType> Rows { get; set; }
public List<JSGridColumn> Columns { get; set; }
public bool AllowEditingRows { get; set; }
public bool AllowDeletingRows { get; set; }
public string UpdateURL { get; set; }
public string DeleteURL { get; set; }
}
public class JSGridColumn
{
public string PropertyName { get; set; }
public ColumnType Type { get; set; }
}
public enum ColumnType
{
Text,
Hidden,
}
Then declare like
var jsGridVM = new JSGridVM<SomeClass1, SomeClass2, SomeClass3>();
OR
var jsGridVM = new JSGridVM<SomeClass1>();
You should declare generic type argument <RowType> not at class level, but at methods: AddTable and GetTable:
public class JSGridVM
{
private Dictionary<Type, object> Tables = new Dictionary<Type, object>();
public JSGridVM AddJSTable<RowType>()
{
Tables.Add(typeof(RowType), new JSTable<RowType>());
return this;
}
public JSTable<RowType> GetJSTable<RowType>()
{
Tables.TryGetValue(typeof(RowType), out object temp);
return (JSTable<RowType>)temp;
}
}
Usage:
var sample = new JSGridVM();
sample.AddJSTable<RowTypeA>().AddJSTable<RowTypeB>();
var test = a.GetJSTable<RowTypeA>();
You could roll your own class, but the .NET DataSet class might be what you are after. DataSet has been around for ages and is still pretty useful.
var ds = new DataSet();
var dataTable = new DataTable("DataTable");
var stringCol = new DataColumn("String Column", typeof(string));
var intCol = new DataColumn("Integer Column", typeof(int));
var decimalCol = new DataColumn("Decimal Column", typeof(decimal));
dataTable.Columns.AddRange(new [] {stringCol, intCol, decimalCol});
var newRow = new object[]
{
"String item",
1,
100.08m
};
dataTable.Rows.Add(newRow);
ds.Tables.Add(dataTable);
var row = ds.Tables["DataTable"].Rows[0];
var stringRowValue = row["String Column"];
var intRowValue = row["Integer Column"];
var decimalRowValue = row["Decimal Column"];
Console.WriteLine($"String value: {stringRowValue}\nInteger value: {intRowValue}\nDecimal Value: {decimalRowValue}");
var rowArr = new DataRow[ds.Tables["DataTable"].Rows.Count];
ds.Tables["DataTable"].Rows.CopyTo(rowArr, 0);
var list = rowArr.ToList();
foreach (DataRow rowitem in list)
{
Console.WriteLine($"\nFrom List: String value: {rowitem["String Column"]}\nInteger value: {rowitem["String Column"]}\nDecimal Value: {rowitem["String Column"]}");
}
Console.ReadKey();
Not sure what you are trying to accomplish, but this is already pretty robust. If you are trying to accomplish a specific use case by creating a generic class, then check out MSDN for more help.
Using Generics:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generic-type-parameters
Func:
https://msdn.microsoft.com/en-us/library/bb549151(v=vs.110).aspx
Why not :
public class JSGridVM<JSTable>
{
public List<JSTable> Tables { get; set; };
}
public class JSTable<?>
{
}
var rowTypeAnimals = AnimalRowType();
var rowTypeBirds = BirdRowType();
var animalTable = new JSTable<AnimalRowType>(){rowTypeAnimals };
var birdTable = new JSTable<BirdRowType>(){rowTypeBirds };
var grid = new JSGridVM(){animalTable,birdTable};
Keep the row type generic for your table and your grid will always have the same JSTable type.

is it possible to access properties by name, which is a variable?

Sorry if this is asked and answered, I searched but think I don't know the vocabulary to find the answer. Researched reflection but that doesn't seem to be the answer here? I'm a novice obviously. I'm trying/making minor contributions to a mod for the new Battletech game.
I've got this Dictionary and would like to use its keys to set properties as in the foreach below. I don't know if this is at compile or runtime, my guess is compile time...
I put *limb* in as pseudo-code for how I'm imagining it might work. The property mechDef.Head is an object of type LocationLoadoutDef with its property CurrentInternalStructure being float.
Hope that makes sense!
Much obliged for any assistance.
public class Settings {
public readonly Dictionary<string, bool> LimbRepair = new Dictionary<string, bool> {
{ "Head", false },
{ "LeftArm", false },
{ "RightArm", false },
{ "CenterTorso", false },
{ "LeftTorso", false },
{ "RightTorso", false },
{ "LeftLeg", false },
{ "RightLeg", false },
};
}
MechDef mechDef = new MechDef
(__instance.DataManager.MechDefs.Get(id), __instance.GenerateSimGameUID());
foreach (string limb in settings.LimbRepair.Keys) {
if (!settings.LimbRepair[limb]) {
mechDef.*limb*.CurrentInternalStructure = Math.Max
(1f, mechDef.*limb*.CurrentInternalStructure * (float)rng.NextDouble());
}
You can do it with Reflection, but....
This is quite easy to do with Reflection, and you'll probably get a couple answers on here that show you how, but since you are writing a game, I'm guessing you want the best performance possible, and Reflection isn't always going to give you that.
Below is a solution that requires no reflection but still allows you to use the loop structure you want. It just requires a little bit of setup when you create the object, then you can access your properties as if they were in a dictionary.
Solution: Use a dictionary of delegates to map the properties
First we need to write a utility class that represents a property. Since properties can be different types, this is a generic class with a type argument.
class PropertyWrapper<T>
{
private readonly Func<T> _getter;
private readonly Action<T> _setter;
public PropertyWrapper(Func<T> getter, Action<T> setter)
{
_getter = getter;
_setter = setter;
}
public T Value
{
get
{
return _getter();
}
set
{
_setter(value);
}
}
}
The idea behind this class is that you create it to represent any property you want, and call its methods to read and set the property. The class knows how to read and set the property because you tell it how, when you construct it, by passing it a short lambda expression that does the work.
This utility will allow you to put all the properties that represent limbs into a dictionary. Then you can look them up by string, just like your settings. So for example your MechDefinition might look like this:
class MechDef
{
public Limb Head { get; set; }
public Limb LeftArm { get; set; }
public Limb RightArm { get; set; }
public Limb LeftTorso { get; set; }
public Limb RightTorso { get; set; }
public Limb CenterTorso { get; set; }
public Limb RightLeg { get; set; }
public Limb LeftLeg { get; set; }
private readonly Dictionary<string, PropertyWrapper<Limb>> Properties;
public MechDef()
{
Properties = new Dictionary<string, PropertyWrapper<Limb>>
{
{"Head", new PropertyWrapper<Limb>( () => Head, v => Head = v ) },
{"LeftArm", new PropertyWrapper<Limb>( () => LeftArm, v => LeftArm = v ) },
{"RightArm", new PropertyWrapper<Limb>( () => RightArm, v => RightArm = v ) },
{"CenterTorso",new PropertyWrapper<Limb>( () => CenterTorso, v => CenterTorso = v )},
{"RightTorso", new PropertyWrapper<Limb>( () => RightTorso, v => RightTorso = v ) },
{"LeftTorso", new PropertyWrapper<Limb>( () => LeftTorso, v => LeftTorso = v ) },
{"RightLeg", new PropertyWrapper<Limb>( () => RightLeg, v => RightLeg = v ) },
{"LeftLeg", new PropertyWrapper<Limb>( () => LeftLeg, v => LeftLeg = v ) }
};
foreach (var property in Properties.Values) property.Value = new Limb();
}
public Limb this[string name]
{
get
{
return Properties[name].Value;
}
set
{
Properties[name].Value = value;
}
}
}
Yes, there is a bit of setup there, but it's all in one place, and it only executes once, when you instantiate the MechDef. Now you can access all of the limbs by string:
foreach (var pair in settings.LimbRepair)
{
if (pair.Value != false) continue;
var limb = mechDef[pair.Key];
limb.CurrentInternalStructure = Math.Max
(
1.0F,
limb.CurrentInternalStructure * (float)rng.NextDouble()
);
}
Link to DotNetFiddle example
You can create a DynamicObject to create your own dynamic Dictionary, See the explanation here
Assume that you want to provide alternative syntax for accessing
values in a dictionary, so that instead of writing
sampleDictionary["Text"] = "Sample text", you can write
sampleDictionary.Text = "Sample text".
This is the example from the same MSDN article above:
public class DynamicDictionary : DynamicObject
{
// The inner dictionary
Dictionary<string, object> dictionary = new Dictionary<string, object>();
public int Count
{
get { return dictionary.Count; }
}
// If you try to get a value of a property not defined
// in the class, this method is called.
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
// Converting the property name to lowercase so
// that property names become case-insensitive.
string name = binder.Name.ToLower();
// If the property name is found in a dictionary, set the result parameter
// to the property value and return true. Otherwise, return false.
return dictionary.TryGetValue(name, out result);
}
// If you try to set a value of a property that is not
// defined in the class, this method is called.
public override bool TrySetMember(SetMemberBinder binder, object value)
{
// Converting the property name to lowercase so that
// property names become case-insensitive.
dictionary[binder.Name.ToLower()] = value;
// You can always add a value to a dictionary, so this method always returns true.
return true;
}
}
And this is how you can use your DynamicDictionary:
dynamic person = new DynamicDictionary();
// Adding new dynamic properties. The TrySetMember method is called.
person.FirstName = "Ellen";
person.LastName = "Adams";
Reflection is one way to get at it. https://stackoverflow.com/a/1954663/83250 actually answers this perfectly. I would however restructure your data so the mechDef object is another dictionary but if you must keep it like your question asks, this will work:
void Main()
{
Dictionary<string, bool> limbRepair = new Dictionary<string, bool>
{
{ "Head", false },
{ "LeftArm", false },
{ "RightArm", false },
// Etc.
};
MechDefinition mechDef = new MechDefinition();
List<Limb> limbs = new List<Limb>();
foreach (KeyValuePair<string, bool> limbsToRepair in limbRepair.Where(x => !x.Value))
{
Limb limb = mechDef.GetPropValue<Limb>(limbsToRepair.Key);
limb.CurrentInternalStructure = 9001;
}
}
public class MechDefinition
{
public MechDefinition()
{
Head = new Limb
{
Id = Guid.NewGuid(),
DateAdded = DateTime.Parse("2018-01-01"),
Name = "Main Head",
CurrentInternalStructure = 8675309
};
}
public Guid Id { get; set; }
public string Name { get; set; }
public int CurrentInternalStructure { get; set; }
public Limb Head { get; set; } = new Limb();
public Limb LeftArm { get; set; } = new Limb();
public Limb RightArm { get; set; } = new Limb();
// etc...
}
public class Limb
{
public Guid Id { get; set; }
public string Name { get; set; }
public DateTime DateAdded { get; set; }
public int CurrentInternalStructure { get; set; }
public bool IsDisabled { get; set; }
}
public static class ReflectionHelpers
{
public static object GetPropValue(this object obj, string name)
{
foreach (string part in name.Split('.'))
{
if (obj == null) { return null; }
Type type = obj.GetType();
PropertyInfo info = type.GetProperty(part);
if (info == null) { return null; }
obj = info.GetValue(obj, null);
}
return obj;
}
public static T GetPropValue<T>(this object obj, string name)
{
object retval = GetPropValue(obj, name);
if (retval == null) { return default(T); }
// throws InvalidCastException if types are incompatible
return (T)retval;
}
}
Be aware that reflection is a very costly operation. If you are dealing with large sets of data, it will be very inefficient. Take a look at https://stackoverflow.com/a/7478557/83250 for a performance overview.
Also code-wise, I prefer to stay away from dynamic and reflection altogether. Reflection has its perks when you need to access a property attribute and dynamic is great if you don't have a strongly typed object. With that said, C# is a strongly typed language and should be treated as such whenever possible. By restructuring your mechDef as a Dictionary<string, Limb> object or something similar you will have a more efficient application.
If I understand correctly, You have something like this:
class LocationLoadoutDef
{
public LocationLoadoutDef()
{
Head = new Prop();
LeftArm = new Prop();
RightArm = new Prop();
CenterTorso = new Prop();
LeftTorso = new Prop();
RightTorso = new Prop();
LeftLeg = new Prop();
RightLeg = new Prop();
}
public Prop Head { get; set; }
public Prop LeftArm { get; set; }
public Prop RightArm { get; set; }
public Prop CenterTorso { get; set; }
public Prop LeftTorso { get; set; }
public Prop RightTorso { get; set; }
public Prop LeftLeg { get; set; }
public Prop RightLeg { get; set; }
...
}
class Prop
{
public float CurrentInternalStructure { get; set; }
...
}
So you can use reflection getting the type of the object and the property.
This is an example based on your pseudocode:
// your instance of LocationLoadoutDef
var mechDef = new LocationLoadoutDef();
//For reflection you need obtain the type
Type mechType = mechDef.GetType();
// loop your Dictionary
foreach (string limb in LimbRepair.Keys)
{
// If the property is false in the dictionary and the type has a property with that name
if (!LimbRepair[limb] && mechType.GetProperties().Any(p => p.Name == limb))
{
// Obtain the instance of the property
var property = mechType.GetProperty(limb).GetValue(mechDef) ;
// Get the property type
Type propertyType = property.GetType();
// If the property has a property CurrentInternalStructure
if (propertyType.GetProperties().Any(p => p.Name == "CurrentInternalStructure"))
{
// Obtain the current value for CurrentInternalStructure
var currentValue = propertyType.GetProperty("CurrentInternalStructure").GetValue(property);
// calculate the new value (I don't know what is rng)
var newValue = 1f ; //Math.Max(1f, (float)currentValue * (float)rng.NextDouble());
// set de value in the property
propertyType.GetProperty("CurrentInternalStructure").SetValue(property, newValue);
}
}
}
You can always create classic and working if .. else or switch.
Or create dictionary with function to update correct property
public class Repair
{
public bool Active { get; set; }
public Action<MechDef> Update { get; set; }
}
public class Settings
{
public readonly Dictionary<string, Repair> LimbRepair =
new Dictionary<string, bool> {
{
"Head",
new Repair { Active = false, mechDef => mechDef.Head.CurrentInternalStructure = yourFunctionForHead }
},
{
"LeftArm",
new Repair { Active = false, mechDef => mechDef.LeftArm.CurrentInternalStructure = yourFunctionForLeftArm }
},
// ... and so on
};
}
Then in the loop you will call correct update action, become much cleaner to use settings class with benefits of strong types and compiler help which prevent dynamic runtime errors
var updates = settings.LimbRepair.Where(pair => pair.Value.Active == false)
.Select(pair => pair.Value);
foreach (var repair in updates)
{
repair.Update();
}

Convert the class as data table using extension method and lambda expression

I am trying to create a extension method to export the class as data-table, in this method I want to give facility to user to export the property with different name in datatable, suppose property name in class is "LoginName" but user want to export it as "Login" in data-table, also user can specify multiple properties to rename.
for example following is the class
public class UserInfo
{
public int UserID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string LoginName { get; set; }
public int CompanyID { get; set; }
}
to export this class as data-table user will use extension method like this
UserInfo us = UserRep.GetUser("userID","Pass");
DataTable userDetails = null;
//Follwing is a pseudo code it could be different in possible manner
userDetails = us.ExportAsDataTable(u=> new {{u.LoginName,"Login"}, {u.CompanyID ,"Company"}});
//Or
userDetails = us.ExportAsDataTable(u=> new { Login = u.LoginName, Company = u.CompanyID});
following ExportAsDataTable metod I have created to do the functionality but unable to give correct expression to take the user input.
public static DataTable ExportAsDataTable<TSource, TProperty>(this TSource instance, Expression<Func<TSource, KeyValuePair<TProperty, string>>> renamePropertyMap)
{
DataTable dataTable = new DataTable();
//Doing export stuff here
return dataTable;
}
//Or
public static DataTable ConvertToDataTable<T>(this T instance, Expression<Func<T, object>> renamePropertyMap) where T : EntityBase
{
//Using this method I am able to get the new name of column from expression like this but not getting the original property name
string columnName = (renamePropertyMap.Body as NewExpression).Members[0].Name
/*note :- result in columnName is "Login" which is fine,
but I need to get orignal property name as well, that is
"LoginName", I am unable to get it from expression.*/
DataTable dataTable = new DataTable();
//Doing export stuff here
return dataTable;
}
I would do something like this instead:
public class FluentBuilder<T>
{
private readonly T _input;
private readonly Dictionary<string, string> _mappings = new Dictionary<string, string>();
public FluentBuilder(T input)
{
_input = input;
}
public FluentBuilder<T> Map(Expression<Func<T, object>> selector, string name)
{
MemberExpression member = selector.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.", selector));
var propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.", selector));
_mappings.Add(propInfo.Name, name);
return this;
}
private string GetName(PropertyInfo prop)
{
string map;
if (_mappings.TryGetValue(prop.Name, out map))
return map;
return prop.Name;
}
public DataTable ToDataTable(string tableName = null)
{
var result = new DataTable(tableName);
foreach (var prop in _input.GetType().GetProperties())
{
result.Columns.Add(GetName(prop));
}
var values = _input.GetType().GetProperties().Select(x => x.GetMethod.Invoke(_input, new object[0])).ToArray();
result.Rows.Add(values);
return result;
}
}
public static class FluentBuilderExtensions
{
public static FluentBuilder<T> SetupWith<T>(this T input)
{
return new FluentBuilder<T>(input);
}
}
class Program
{
public class UserInfo
{
public string MailAddress { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
static void Main(string[] args)
{
var userInfo = new UserInfo()
{
MailAddress = "foo#bar.com",
Username = "foouser",
Password = "barpassword"
};
var dt = userInfo.SetupWith()
.Map(x => x.MailAddress, "address")
.Map(x => x.Username, "user")
.ToDataTable();
}
}
MoreLINQ already provides ToDatatable() to convert an IEnumerable result to a DataTable. The method is available as part of the full NuGet package or as a source package that you can add to your project.
To generate a DataTable from a subset of UserInfo properties, use a Select before calling ToDataTable(), eg :
var table = myUserInfos.Select(us=>new {Login=us.LoginName, Company=us.CompanyID})
.ToDataTable();
If you only have one item and want to convert it to a single-row DataTable:
That's a very strange request. Why do you want to do that instead of eg binding the single object to the UI controls?
You can wrap it in an array eg:
var table = new[]{theUser}.Select(us=>new {Login=us.LoginName, Company=us.CompanyID})
.ToDataTable();
I used DataAnnotations and Reflection.
You need to add reference of missing libraries from following. I tested this code and it is working.
using System;
using System.Data;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var user = new UserInfo();
var name = GetAttributeFrom<DisplayAttribute>(user, "LoginName").Name;
var dt = objToDataTable(user);
Console.ReadLine();
}
public static DataTable objToDataTable(UserInfo obj)
{
DataTable dt = new DataTable();
UserInfo objU = new UserInfo();
foreach (PropertyInfo info in typeof(UserInfo).GetProperties())
{
dt.Columns.Add(GetAttributeFrom<DisplayAttribute>(objU, info.Name).Name);
}
dt.AcceptChanges();
return dt;
}
public static T GetAttributeFrom<T>(object instance, string propertyName) where T : Attribute
{
var attrType = typeof(T);
var property = instance.GetType().GetProperty(propertyName);
return (T)property.GetCustomAttributes(attrType, false).First();
}
}
public class UserInfo
{
[Display(Name = "ID")]
public int UserID { get; set; }
[Display(Name = "FName")]
public string FirstName { get; set; }
[Display(Name = "LName")]
public string LastName { get; set; }
[Display(Name = "Login")]
public string LoginName { get; set; }
[Display(Name = "Company")]
public int CompanyID { get; set; }
}
This is how I build the extension method to convert the class as datatable
public static class EntityExtensions
{
private static readonly string expressionCannotBeNullMessage = "The expression cannot be null.";
private static readonly string invalidExpressionMessage = "Invalid expression.";
public static DataTable ConvertToDataTable<T>(this T instance, Expression<Func<T, object>> proprtiesToSkip = null, Expression<Func<T, object>> proprtiesToRename = null) where T : EntityBase
{
string columnName = "";
string orgPropName;
int counter = 0;
Dictionary<string, string> renameProperties = null;
MemberInfo newName = null;
NewExpression expression = null;
List<string> skipProps = null;
try
{
if (proprtiesToSkip != null )
{
if (proprtiesToSkip.Body is NewExpression)
{
skipProps = new List<string>();
expression = (proprtiesToSkip.Body as NewExpression);
foreach (var cExpression in expression.Arguments)
{
skipProps.Add(GetMemberName(cExpression));
}
}
else
{
throw new ArgumentException("Invalid expression supplied in proprtiesToSkip while converting class to datatable");
}
}
if (proprtiesToRename != null)
{
if (proprtiesToRename.Body is NewExpression)
{
renameProperties = new Dictionary<string, string>();
expression = (proprtiesToRename.Body as NewExpression);
foreach (var cExpression in expression.Arguments)
{
newName = expression.Members[counter];
orgPropName = GetMemberName(cExpression);
renameProperties.Add(orgPropName, newName.Name);
counter++;
}
}
else
{
throw new ArgumentException("Invalid expression supplied in proprtiesToRename while converting class to datatable");
}
}
var properties = instance.GetType().GetProperties().Where(o =>
{
return (skipProps != null && !skipProps.Contains(o.Name, StringComparer.OrdinalIgnoreCase) &&
(o.PropertyType != typeof(System.Data.DataTable) && o.PropertyType != typeof(System.Data.DataSet)));
}).ToArray();
DataTable dataTable = new DataTable();
foreach (PropertyInfo info in properties)
{
columnName = "";
if (renameProperties != null && renameProperties.ContainsKey(info.Name))
{
columnName = renameProperties[info.Name];
}
if (string.IsNullOrEmpty(columnName))
{
columnName = info.Name;
}
dataTable.Columns.Add(new DataColumn(columnName, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
}
object[] values = new object[properties.Length];
for (int i = 0; i < properties.Length; i++)
{
values[i] = properties[i].GetValue(instance);
}
dataTable.Rows.Add(values);
return dataTable;
}
finally
{
renameProperties = null;
newName = null;
expression = null;
skipProps = null;
}
}
private static string GetMemberName(Expression expression)
{
if (expression == null)
{
throw new ArgumentException(expressionCannotBeNullMessage);
}
if (expression is MemberExpression)
{
// Reference type property or field
var memberExpression = (MemberExpression)expression;
return memberExpression.Member.Name;
}
throw new ArgumentException(invalidExpressionMessage);
}
}
Calling it like this
class Program
{
static void Main(string[] args)
{
CancelReason ad = new CancelReason();
ad.BusinessLineID = 1;
ad.CancelReasonCodeID = 2;
ad.CancelRefund = "C";
ad.CompanyID = 0;
ad.DBOperation = 1;
ad.Description = "test";
ad.IsActive = "Y";
ad.ReasonCode = "TestCode";
ad.UpdateStamp = new byte[] { 1, 2, 3, 4 };
DataTable dt = ad.ConvertToDataTable(s => new { s.DBOperation, s.BusinessLineID, s.LoggedInUser }, r => new { Refund = r.CancelRefund, Company = r.CompanyID });
}
}

Get subclass content from object

I have the following classes
public enum Category { foo, foo1, foo2 }
public class Event
{
public DateTime Timestamp { get; set; } = DateTime.Now;
public string GameTime { get; set; }
public string Content { get; set; }
public Person Author { get; set; }
public Category Category { get; set; }
}
and
public class MemberEvent : Event
{
public Member Person { get; set; }
}
The object is created correctly, but if I want to call "Person", this is not displayed to me. If I have a var match, I can call for example match[0].Timestamp but not match[0].Person. The Event object is stored in a List, therefore also the index. I feel I'm missing something simple.
UPDATE: The Code that create the Object
var match = SessionController.Instance.Current;
DataTable dt = dataGrid.ItemsSource as DataTable;
foreach (System.Data.DataRow item in dt.Rows)
{
var memberFoo = new MemberEvent();
memberFoo.Category = Category.Warning;
memberFoo.Time = item["Time"].ToString();
var person = new Person();
person.FirstName = item["FirstName"].ToString();
person.LastName = item["LastName"].ToString();
var passport = new Passport();
passport.Active = true;
passport.PassNumber = item["Pass"].ToString();
passport.Player = person;
memberFoo.Person = passport;
match.Match.Events.Add(memberFoo);
}
SessionController.Instance.Current = match;
Cast your instance to the expected type and test for null to guard for the unexpected:
var memberEvent = match[0] as MemberEvent;
if (memberEvent != null)
{
Console.WriteLine(memberEvent.Person)
}
You will have to cast your Event into a MemberEvent if you want to access the Person property. It is not possible to get the property from Event.
List<Event> myEvents = GetMyEvents();
var myMemberEvent = (MemberEvent)myEvent[0];
It can cause an exception if it can't convert to MemberEvent.

Get Values From Complex Class Using Reflection

I have a class, which is created and populated from an xml string, I've simplified it for example purposes:
[XmlRoot("Person")]
public sealed class Person
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("Location")]
public string Location { get; set; }
[XmlElement("Emails", Type = typeof(PersonEmails)]
public PersonEmails Emails { get; set; }
}
public class PersonEmails
{
[XmlElement("Email", Type = typeof(PersonEmail))]
public PersonEmail[] Emails { get; set; }
}
public class PersonEmail
{
[XmlAttribute("Type")]
public string Type { get; set; }
[XmlText]
public string Value { get; set; }
}
To extract the information, I'm trying to load them into another class, which is simply:
public class TransferObject
{
public string Name { get; set; }
public ObjectField[] Fields { get; set; }
}
public class ObjectField
{
public string Name { get; set; }
public string Value { get; set; }
}
I'm only populating "Fields" from the other object, which would simply be (Name = "Location", Value = "London"), but for Emails, (Name = "Email"+Type, Value = jeff#here.com)
Currently I can populate all the other fields, but I'm stuck with Emails, and knowing how to dig deep enough to be able to use reflection (or not) to get the information I need. Currently I'm using:
Person person = Person.FromXmlString(xmlString);
List<ObjectField> fields = new List<ObjectField>();
foreach (PropertyInfo pinfo in person.getType().GetProperties()
{
fields.Add(new ObjectField { Name = pinfo.Name, Value = pinfo.getValue(person, null).ToString();
}
How can I expand on the above to add all my emails to the list?
You are trying to type cast a complex values type to string value so you lost the data. Instead use following code:
class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Name = "Person One";
person.Location = "India";
person.Emails = new PersonEmails();
person.Phones = new PersonPhones();
person.Emails.Emails = new PersonEmail[] { new PersonEmail() { Type = "Official", Value = "xyz#official.com" }, new PersonEmail() { Type = "Personal", Value = "xyz#personal.com" } };
person.Phones.Phones = new PersonPhone[] { new PersonPhone() { Type = "Official", Value = "789-456-1230" }, new PersonPhone() { Type = "Personal", Value = "123-456-7890" } };
List<ObjectField> fields = new List<ObjectField>();
fields = GetPropertyValues(person);
}
static List<ObjectField> GetPropertyValues(object obj)
{
List<ObjectField> propList = new List<ObjectField>();
foreach (PropertyInfo pinfo in obj.GetType().GetProperties())
{
var value = pinfo.GetValue(obj, null);
if (pinfo.PropertyType.IsArray)
{
var arr = value as object[];
for (var i = 0; i < arr.Length; i++)
{
if (arr[i].GetType().IsPrimitive)
{
propList.Add(new ObjectField() { Name = pinfo.Name + i.ToString(), Value = arr[i].ToString() });
}
else
{
var lst = GetPropertyValues(arr[i]);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
else
{
if (pinfo.PropertyType.IsPrimitive || value.GetType() == typeof(string))
{
propList.Add(new ObjectField() { Name = pinfo.Name, Value = value.ToString() });
}
else
{
var lst = GetPropertyValues(value);
if (lst != null && lst.Count > 0)
propList.AddRange(lst);
}
}
}
return propList;
}
}
Check this snippet out:
if(pinfo.PropertyType.IsArray)
{
// Grab the actual instance of the array.
// We'll have to use it in a few spots.
var array = pinfo.GetValue(personObject);
// Get the length of the array and build an indexArray.
int length = (int)pinfo.PropertyType.GetProperty("Length").GetValue(array);
// Get the "GetValue" method so we can extact the array values
var getValue = findGetValue(pinfo.PropertyType);
// Cycle through each index and use our "getValue" to fetch the value from the array.
for(int i=0; i<length; i++)
fields.Add(new ObjectField { Name = pinfo.Name, Value = getValue.Invoke(array, new object[]{i}).ToString();
}
// Looks for the "GetValue(int index)" MethodInfo.
private static System.Reflection.MethodInfo findGetValue(Type t)
{
return (from mi in t.GetMethods()
where mi.Name == "GetValue"
let parms = mi.GetParameters()
where parms.Length == 1
from p in parms
where p.ParameterType == typeof(int)
select mi).First();
}
You can definately do it with Reflection... You can take advantage of the fact that a Type can tell you if it's an array or not (IsArray)... and then take advantage of the fact that an Array has a method GetValue(int index) that will give you a value back.
Per your comment
Because Emails is a property within a different class, recursion should be used. However the trick is knowing when to go to the next level. Really that is up to you, but
if it were me, I would use some sort of Attribute:
static void fetchProperties(Object instance, List<ObjectField> fields)
{
foreach(var pinfo in instance.GetType().GetProperties())
{
if(pinfo.PropertyType.IsArray)
{
... // Code described above
}
else if(pinfo.PropertyType.GetCustomAttributes(typeof(SomeAttribute), false).Any())
// Go the next level
fetchProperties(pinfo.GetValue(instance), fields);
else
{
... // Do normal code
}
}
}

Categories