this is a very simple question.
such as this code:
if(o == null)
{
o = new { };
}
PropertyInfo[] p1 = o.GetType().GetProperties();
foreach(PropertyInfo pi in p1)
{}
but like this:
ModelA.ModelB.ModelC.ModelD.ModelE
how to get ModelE's value by reflect ModelA
There is a solution explained here:
using a helper method:
public static class ReflectionHelper
{
public static Object GetPropValue(this Object obj, String propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return obj.GetType().GetProperty(propName).GetValue(obj, null);
}
foreach (String part in nameParts)
{
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;
}
}
then the method can be used like this:
ModelA obj = new ModelA { */....*/ };
obj.GetPropValue("modelB.modelC.modelD.modelE");
please note that you should pass the property names to the function not the class names.
using nested function to do it like following:
var testObj = new
{
nameA = "A",
ModelB = new
{
nameB = "B",
ModelC = new
{
NameC = "C",
}
}
};
var result = ParseProperty(testObj, null, "ModelA");
public Dictionary<string, object> ParseProperty(object o, Dictionary<string, object> result, string preFix = null)
{
result = result ?? new Dictionary<string, object>();
if (o == null) return result;
Type t = o.GetType();
//primitive type or value type or string or nested return
if (t.IsPrimitive || t.IsValueType || t.FullName == "System.String" || t.IsNested) return result;
var proerties = o.GetType().GetProperties();
foreach (var property in proerties)
{
var value = property.GetValue(o);
result.Add($"{preFix}.{property.Name}", value);
//nested call
ParseProperty(value, result, $"{preFix}.{property.Name}");
}
return result;
}
I assume these are all anonymous types because otherwise you could just do GetProperties() on the type of ModelE.
So you basically have to next 5 of your loops like
foreach (PropertyInfo pi1 in o1.GetType().GetProperties())
{
if (pi.Name = "ModelB") // or some other criterion
{
o2 = pi1.GetValue(o1);
foreach (PropertyInfo pi2 in o2.GetType().GetProperties())
{
if (pi.Name = "ModelC") // or some other criterion
{
o3 = pi1.GetValue(o2);
// and so on
}
}
}
}
Related
i want to get a string from user and show him a list of enums member and its value.
for example i have this enum
public enum exampleEnum
{
[MyCustomProperty(customName = "نام1")]
member1 = 1,
[MyCustomProperty(customName = "نام2")]
member2 = 2,
}
help me for create this function
public List<enumResult> GetEnumDetailWithName(string enumName)
{
???
return result;// {name:'member1',value:1,customName='نام1'},{name:'member2',value:2,customName='نام2'}
}
i have write this code but not complete
in this lines get enum from all assembly and its done
then get all members of enum currectly
then for any member of enum add a member to result list , filling name is true but i cant fill Value and CustomName Field ...
public List<enumDetail> GetEnumDetailWithName(string enumName)
{
var enumFullName = $"Bamdad.PublicEnum.Enums+{enumName}";
var assemblyList = AppDomain.CurrentDomain.GetAssemblies();
Type type = null;
foreach (var assembly in assemblyList)
{
type = assembly.GetType(enumFullName);
if (type == null)
continue;
if (type.IsEnum)
break;
}
if (type == null)
return null;
//until this line get enum currectly
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Static).Where(q=> q?.DeclaringType?.Name == enumName).ToList();
var properties = type.GetProperties();
if (!members.Any()) return null;
var result = new List<enumDetail>();
//get members currectly
foreach (var mem in members)
{
var resultItem = new enumDetail()
{
Name = mem.Name, // true
Value = 0, // i cant get
CustomName = "???" // i cant get
};
result.Add(resultItem);
}
return result;
}
please help me
for this question i should use Enum.GetValues and Enum.GetName , for getting custom attribute write a function and use it
public List<enumDetail> GetEnumMemberFromString(string enumName)
{
var enumFullName = $"Bamdad.PublicEnum.Enums+{enumName}";
var assemblyList = AppDomain.CurrentDomain.GetAssemblies();
Type type = null;
foreach (var assembly in assemblyList)
{
type = assembly.GetType(enumFullName);
if (type == null)
continue;
if (type.IsEnum)
break;
}
if (type == null) return null;
var valuesToNames = Enum.GetValues(type)
.Cast<object>()
.ToDictionary(q => Enum.GetName(type, q),q=> (int)q);
var result = valuesToNames.Select(q => new enumDetail()
{
Name = q.Key,
Value = q.Value,
CustomName = type.GetMyCustomProperty(q.Key)?.ToString() ?? q.Key
}).ToList();
return result;
}
this function is for getting custom attribute
public static object GetMyCustomProperty(this Type type,string member = "")
{
if (type == null)
return "";
MemberInfo[] memInfo = type.GetMember(member);
if (memInfo.Length <= 0) return member;
object[] attrs = memInfo[0].GetCustomAttributes(typeof(MyCustomPropertyAttribute), false);
if (!attrs.Any())
return null;
var result = ((MyCustomPropertyAttribute)attrs[0]);
return result.customName;
}
I have a GetDynamicParameters() on cmdlet Get-DateSlain that does something like this:
public object GetDynamicParameters()
{
List<string> houseList = {"Stark", "Lannister", "Tully"};
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a house name",
},
new ValidateSetAttribute(houseList.ToArray()),
};
if (!this.ContainsKey("House"))
{
this.runtimeParameters.Add("House", new RuntimeDefinedParameter("House", typeof(string), attributes));
}
}
And this works as expected - users can type Get-DateSlain -House, and tab through the available houses. However, once a house is chosen, I want to be able to narrow down the results to characters in that house. Furthermore, if it's house 'Stark', I want to allow a -Wolf parameter. So to implement (some value validity checks removed for brevity):
public object GetDynamicParameters()
{
if (this.runtimeParameters.ContainsKey("House"))
{
// We already have this key - no need to re-add. However, now we can add other parameters
var house = this.runtimeParameters["House"].Value.ToString();
if (house == "Stark")
{
List<string> characters = { "Ned", "Arya", "Rob" };
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a character name",
},
new ValidateSetAttribute(characters.ToArray()),
};
this.runtimeParameters.Add("Character", new RuntimeDefinedParameter("Character", typeof(string), attributes));
List<string> wolves = { "Shaggydog", "Snow", "Lady" };
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a wolf name",
},
new ValidateSetAttribute(wolves.ToArray()),
};
this.runtimeParameters.Add("Wolf", new RuntimeDefinedParameter("Wolf", typeof(string), attributes));
}
else if (house == "Lannister")
{
List<string> characters = { "Jaimie", "Cersei", "Tywin" };
// ...
}
// ...
return this.runtimeParameters;
}
List<string> houseList = {"Stark", "Lannister", "Tully"};
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a house name",
},
new ValidateSetAttribute(houseList.ToArray()),
};
this.runtimeParameters.Add("House", new RuntimeDefinedParameter("House", typeof(string), attributes));
}
This looks like it should work, but it doesn't. The GetDynamicParameters function is only called once, and that is before a value is supplied to this.runtimeParameters["House"]. Since it doesn't re-evaluate after that value is filled in, the additional field(s) are never added, and any logic in ProcessRecord that relies on these fields will fail.
So - is there a way to have multiple dynamic parameters that rely on each other?
Have a look a the aswer to this question, it shows a way to access the values of other dynamic parameters in the GetDynamicParameters method:
Powershell module: Dynamic mandatory hierarchical parameters
I adapted the code from the mentioned answer so it can handle SwitchParameters and the raw input parameter is converted to the actual type of the cmdlet parameter. It does not work if the dynamic parameter you want to get the value for is passed via pipeline. I think that is not possible because dynamic parameters are always created before pipeline input is evaluated. Here it is:
public static class DynamicParameterExtension
{
public static T GetUnboundValue<T>(this PSCmdlet cmdlet, string paramName, int unnamedPosition = -1))
{
var context = TryGetProperty(cmdlet, "Context");
var processor = TryGetProperty(context, "CurrentCommandProcessor");
var parameterBinder = TryGetProperty(processor, "CmdletParameterBinderController");
var args = TryGetProperty(parameterBinder, "UnboundArguments") as System.Collections.IEnumerable;
if (args != null)
{
var isSwitch = typeof(SwitchParameter) == typeof(T);
var currentParameterName = string.Empty;
object unnamedValue = null;
var i = 0;
foreach (var arg in args)
{
var isParameterName = TryGetProperty(arg, "ParameterNameSpecified");
if (isParameterName != null && true.Equals(isParameterName))
{
var parameterName = TryGetProperty(arg, "ParameterName") as string;
currentParameterName = parameterName;
if (isSwitch && string.Equals(currentParameterName, paramName, StringComparison.OrdinalIgnoreCase))
{
return (T)(object)new SwitchParameter(true);
}
continue;
}
var parameterValue = TryGetProperty(arg, "ArgumentValue");
if (currentParameterName != string.Empty)
{
if (string.Equals(currentParameterName, paramName, StringComparison.OrdinalIgnoreCase))
{
return ConvertParameter<T>(parameterValue);
}
}
else if (i++ == unnamedPosition)
{
unnamedValue = parameterValue;
}
currentParameterName = string.Empty;
}
if (unnamedValue != null)
{
return ConvertParameter<T>(unnamedValue);
}
}
return default(T);
}
static T ConvertParameter<T>(this object value)
{
if (value == null || Equals(value, default(T)))
{
return default(T);
}
var psObject = value as PSObject;
if (psObject != null)
{
return psObject.BaseObject.ConvertParameter<T>();
}
if (value is T)
{
return (T)value;
}
var constructorInfo = typeof(T).GetConstructor(new[] { value.GetType() });
if (constructorInfo != null)
{
return (T)constructorInfo.Invoke(new[] { value });
}
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (Exception)
{
return default(T);
}
}
static object TryGetProperty(object instance, string fieldName)
{
if (instance == null || string.IsNullOrEmpty(fieldName))
{
return null;
}
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;
var propertyInfo = instance.GetType().GetProperty(fieldName, bindingFlags);
try
{
if (propertyInfo != null)
{
return propertyInfo.GetValue(instance, null);
}
var fieldInfo = instance.GetType().GetField(fieldName, bindingFlags);
return fieldInfo?.GetValue(instance);
}
catch (Exception)
{
return null;
}
}
}
So for your example you should be able to use it like:
public object GetDynamicParameters()
{
var houseList = new List<string> { "Stark", "Lannister", "Tully" };
var attributes = new Collection<Attribute>
{
new ParameterAttribute { HelpMessage = "Enter a house name" },
new ValidateSetAttribute(houseList.ToArray()),
};
var runtimeParameters = new RuntimeDefinedParameterDictionary
{
{"House", new RuntimeDefinedParameter("House", typeof (string), attributes)}
};
var selectedHouse = this.GetUnboundValue<string>("House");
//... add parameters dependant on value of selectedHouse
return runtimeParameters;
}
After all I'm not sure if it's a good idea trying to get those dynamic parameter values in the first place. It is obviously not supported by PowerShell Cmdlet API (see all the reflection to access private members in the GetUnboundValue method), you have to reimplement the PowerShell parameter conversion magic (see ConvertParameter, I'm sure I missed some cases there) and there is the restriction with pipelined values. Usage at your own risk:)
I have the problem, that i don't know how to call the this property of an object instance over reflection.
I have the following method:
public object GetValue(SOP sop, object value)
{
// 1. find the this getter/setter method for the value parameter
// 2. Invoke the method and pass the result of GetIndexArray(sop) as a parameter
// 3. return the result of the method
}
which should call the this property of the value-instance and return the value the property returns.
The problem is, that i don't know the type of the value-instance, which is passed as a parameter.
The index which should be passed to the this property is given over the following method:
private object[] GetIndexArray(SOP sop)
But i don't know how to call the this-property. The object-instance can be anything, a string, Dictionary, ...
Does any one has an idea, how to solve the problem?
EDIT:
The GetValue method should do the follwing task, but dynamically over reflection:
public object GetValue(SOP sop, object value)
{
// Non relfection, lets say 'value' is a Dicitionary<string, string>
// return value["key"];
// The type of 'value' is unknown, and the index-parameter (the keys) are accessable over GetIndexArray()
}
EDIT2:
In c# every getter and setter can be invoked via reflection as a method. Is there a way, to get the methods for the "this" property? If it's possible, the problem can be solved with invoking the mesthods.
Call of the GetValue method
object pInst = parentInstance.GetType().GetProperty(VariableName, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).GetValue(parentInstance, null);
var indexChldren = FindChildrenOfType<IndexNode>();
if (indexChldren.Count > 0)
{
pInst = indexChldren[0].GetValue(sop, pInst);
}
I decided to remake the post, since my previous one didn't correctly do what it was supposed to do.
To check which indexers are on an object, you can use the following, which will
Return null when object is null
Return an empty IList when no indexers are found
Return a list of MethodInfos with the found indexers
The trick to check it, is to check if a property has any GetIndexParameters();
public IList<MethodInfo> GetIndexProperties(object obj)
{
if (obj == null)
{
return null;
}
var type = obj.GetType();
IList<MethodInfo> results = new List<MethodInfo>();
try
{
var props = type.GetProperties(System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Instance);
if (props != null)
{
foreach (var prop in props)
{
var indexParameters = prop.GetIndexParameters();
if (indexParameters == null || indexParameters.Length == 0)
{
continue;
}
var getMethod = prop.GetGetMethod();
if (getMethod == null)
{
continue;
}
results.Add(getMethod);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return results;
}
This works with a List, Dictionary, string, object with indexers, indexers defined with IndexerNameAttribute
As an example, i used this MainMethod, to try several possibilities
object[] exampleHayStack = new object[] {
"This is a test of an indexer",
new TestIndexer(),
null,
string.Empty,
new ClassIndexer(),
new Dictionary<string, string>() { { "key", "value" } },
new List<string>() { "A", "B", "C", "D", "E", "F", "G" } };
ClassIndexer myIndexer = new ClassIndexer();
foreach (var obj in exampleHayStack)
{
var methods = myIndexer.GetIndexProperties(obj);
if (methods == null || methods.Count == 0)
{
Console.WriteLine("{0} doesn't have any indexers", obj);
continue;
}
Console.WriteLine("Testing {0}", obj);
foreach (MethodInfo mi in methods)
{
IList<object> indexParams = new List<object>();
var requiredParams = mi.GetParameters();
foreach (var par in requiredParams)
{
indexParams.Add(myIndexer.ParamForObject(obj, par));
}
try
{
var result = mi.Invoke(obj, indexParams.ToArray());
Console.WriteLine("Result of requesting ({0}) = {1}", string.Join(",", indexParams), result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Console.ReadLine();
Which then results into:
Testing This is a test of an indexer
Result of requesting (21) = i
Testing TestReflection.Program+TestIndexer
Result of requesting (53) = 5
Result of requesting (Key) = Key item
doesn't have any indexers
Testing
Exception has been thrown by the target of an invocation.
TestReflection.Program+ClassIndexer doesn't have any indexers
Testing System.Collections.Generic.Dictionary`2[System.String,System.String]
Result of requesting (key) = value
Testing System.Collections.Generic.List`1[System.String]
Result of requesting (5) = F
The complete implementation of the ClassIndexer you can find here, it contains an extra method for getting possible key values (but those you have already)
public class ClassIndexer
{
Random pNext = new Random();
public IList<MethodInfo> GetIndexProperties(object obj)
{
if (obj == null)
{
return null;
}
var type = obj.GetType();
IList<MethodInfo> results = new List<MethodInfo>();
try
{
var props = type.GetProperties(System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Instance);
if (props != null)
{
foreach (var prop in props)
{
var indexParameters = prop.GetIndexParameters();
if (indexParameters == null || indexParameters.Length == 0)
{
continue;
}
var getMethod = prop.GetGetMethod();
if (getMethod == null)
{
continue;
}
results.Add(getMethod);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return results;
}
public object ParamForObject(object obj, ParameterInfo pi)
{
if (obj is IDictionary)
{
int maxNumber = ((IDictionary)obj).Keys.Count;
if (pi.ParameterType.Equals(typeof(int)))
{
return pNext.Next(maxNumber);
}
if (pi.ParameterType.Equals(typeof(string)))
{
int target = pNext.Next(maxNumber);
foreach (var key in ((IDictionary)obj).Keys)
{
target--;
if (target <= 0)
{
return key;
}
}
return null;
}
}
if (obj is string)
{
if (pi.ParameterType.Equals(typeof(int)))
{
return pNext.Next((obj as string).Length);
}
}
if (obj is IList)
{
return pNext.Next(((IList)obj).Count);
}
if (pi.ParameterType.Equals(typeof(string)))
{
return "Key";
}
if (pi.ParameterType.Equals(typeof(int)))
{
return pNext.Next(100);
}
return null;
}
public ClassIndexer()
{
}
}
For the rest, this was a good research, thanks for the question !
I created a DataRow on my project:
DataRow datarow;
I want to convert this DataRow to any Type of Object.
How could I do it?
This is a pretty cool way I use it.
public static T ToObject<T>(this DataRow dataRow)
where T : new()
{
T item = new T();
foreach (DataColumn column in dataRow.Table.Columns)
{
PropertyInfo property = GetProperty(typeof(T), column.ColumnName);
if (property != null && dataRow[column] != DBNull.Value && dataRow[column].ToString() != "NULL")
{
property.SetValue(item, ChangeType(dataRow[column], property.PropertyType), null);
}
}
return item;
}
private static PropertyInfo GetProperty(Type type, string attributeName)
{
PropertyInfo property = type.GetProperty(attributeName);
if (property != null)
{
return property;
}
return type.GetProperties()
.Where(p => p.IsDefined(typeof(DisplayAttribute), false) && p.GetCustomAttributes(typeof(DisplayAttribute), false).Cast<DisplayAttribute>().Single().Name == attributeName)
.FirstOrDefault();
}
public static object ChangeType(object value, Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return null;
}
return Convert.ChangeType(value, Nullable.GetUnderlyingType(type));
}
return Convert.ChangeType(value, type);
}
I Have found one solution for my application.
// function that creates an object from the given data row
public static T CreateItemFromRow<T>(DataRow row) where T : new()
{
// create a new object
T item = new T();
// set the item
SetItemFromRow(item, row);
// return
return item;
}
public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
{
// go through each column
foreach (DataColumn c in row.Table.Columns)
{
// find the property for the column
PropertyInfo p = item.GetType().GetProperty(c.ColumnName);
// if exists, set the value
if (p != null && row[c] != DBNull.Value)
{
p.SetValue(item, row[c], null);
}
}
}
This will map your DataRow to ViewModel, Like below.
Your_ViewModel model = CreateItemFromRow<Your_ViewModel>(row);
class Person{
public string FirstName{get;set;}
public string LastName{get;set;}
}
Person person = new Person();
person.FirstName = dataRow["FirstName"] ;
person.LastName = dataRow["LastName"] ;
or
Person person = new Person();
person.FirstName = dataRow.Field<string>("FirstName");
person.LastName = dataRow.Field<string>("LastName");
Similar to some of the previous approaches, I created this extension method for DataRow which takes an argument object to be populated. Main difference is that in addition to populating object's Properties, it also populates Fields of given object. This should also work for simpler structures (Though I only tested on objects).
public static T ToObject<T>( this DataRow dataRow )
where T : new() {
T item = new T();
foreach( DataColumn column in dataRow.Table.Columns ) {
if( dataRow[column] != DBNull.Value ) {
PropertyInfo prop = item.GetType().GetProperty( column.ColumnName );
if( prop != null ) {
object result = Convert.ChangeType( dataRow[column], prop.PropertyType );
prop.SetValue( item, result, null );
continue;
}
else {
FieldInfo fld = item.GetType().GetField( column.ColumnName );
if( fld != null ) {
object result = Convert.ChangeType( dataRow[column], fld.FieldType );
fld.SetValue( item, result );
}
}
}
}
return item;
}
You can put this code in your current class or in a global static class.
It needs following namespaces...
using System;
using System.Data;
using System.Reflection;
Usage is as simple as...
MyClassName obj = dataRow.ToObject<MyClassName>()
Here is an extension method that would allow you to convert a DataRow to a given object.
public static class DataRowExtensions
{
public static T Cast<T>(this DataRow dataRow) where T : new()
{
T item = new T();
IEnumerable<PropertyInfo> properties = item.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.CanWrite);
foreach (DataColumn column in dataRow.Table.Columns)
{
if (dataRow[column] == DBNull.Value)
{
continue;
}
PropertyInfo property = properties.FirstOrDefault(x => column.ColumnName.Equals(x.Name, StringComparison.OrdinalIgnoreCase));
if (property == null)
{
continue;
}
try
{
Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
object safeValue = (dataRow[column] == null) ? null : Convert.ChangeType(dataRow[column], t);
property.SetValue(item, safeValue, null);
}
catch
{
throw new Exception($"The value '{dataRow[column]}' cannot be mapped to the property '{property.Name}'!");
}
}
return item;
}
}
And you can use the above extension method like so
foreach (DataRow row in dataTable.Rows)
{
SomeClassType obj = row.Cast<SomeClassType>();
// do something with your object
}
Given Converter<TIn, TOut> is a delegate, then the following should work:
List<Person> personList = new List<Person>();
personList = ConvertDataRowToList(ds, (row) => {
return new Person
{
FirstName = row["FirstName"],
LastName = row["LastName"]
// Rest of properties should assign here...
};
});
https://learn.microsoft.com/en-us/dotnet/api/system.converter-2
Apart from the manual method Avi shows, you can use a mapping system like AutoMapper to do the transformation for you. This is particularly useful in the case where you have a lot of columns/properties to map.
Check out this article on how to use AutoMapper to convert a DataTable to a list of objects.
DataRow has a property ItemArray, which contains an array of object values. You can work with this array and create any custom type with the values from your DataRow.
With less complications ;), two steps will solve the task:
1. cast to dictionary (ToDictionary).
2. map dictionary to entity (MapToEntity).
public static IDictionary<string, object> ToDictionary(
this DataRow content
)
{
var values = content.ItemArray;
var columns = content
.Table
.Columns
.Cast<DataColumn>()
.Select(x => x.ColumnName);
return values
.Select((v, m) => new { v, m })
.ToDictionary(
x => columns.ElementAt(x.m)
, x => (x.v == DBNull.Value ? null : x.v)
);
}
public static T MapToEntity<T>(
this IDictionary<string, object> source
)
where T : class, new()
{
// t - target
T t_object = new T();
Type t_type = t_object.GetType();
foreach (var kvp in source)
{
PropertyInfo t_property = t_type.GetProperty(kvp.Key);
if (t_property != null)
{
t_property.SetValue(t_object, kvp.Value);
}
}
return t_object;
}
...and the usage would be:
DataRow dr = getSomeDataRow(someArgs);
ABC result = dr.ToDictionary()
.MapToEntity<ABC>();
You could convert the whole Data table into a list Object like the code below. Of course, you can take the specific object which you want with the index or the field value.
/// <summary>
/// convert a datatable to list Object
/// </summary>
/// <typeparam name="T">object model</typeparam>
/// <param name="dataTable"></param>
/// <returns>ex ussage: List<User> listTbl = CommonFunc.convertDatatblToListObj<User>(dataTable);</returns>
public static List<T> convertDatatableToListObject<T>(DataTable dataTable)
{
List<T> res = new List<T>();
try
{
string tblJson = JsonConvert.SerializeObject(dataTable);
res = JsonConvert.DeserializeObject<List<T>>(tblJson);
}
catch (Exception ex)
{
string exStr = ex.Message;
}
return res;
}
With these changes worked fine for me, for fields int, long, int? and long?
// function that creates an object from the given data row
public static T CreateItemFromRow<T>(DataRow row) where T : new()
{
// create a new object
T item = new T();
// set the item
SetItemFromRow(item, row);
// return
return item;
}
public static void SetItemFromRow<T>(T item, DataRow row) where T : new()
{
// go through each column
foreach (DataColumn c in row.Table.Columns)
{
// find the property for the column
PropertyInfo p = item.GetType().GetProperty(c.ColumnName);
// if exists, set the value
if (p != null && row[c] != DBNull.Value)
{
if (p.PropertyType.Name == "Int64")
{
p.SetValue(item, long.Parse(row[c].ToString()), null);
}
else if (p.PropertyType.Name == "Int32")
{
p.SetValue(item, int.Parse(row[c].ToString()), null);
}
else if (p.PropertyType.FullName.StartsWith("System.Nullable`1[[System.Int32"))
{
p.SetValue(item, (int?)int.Parse(row[c].ToString()), null);
}
else if (p.PropertyType.FullName.StartsWith("System.Nullable`1[[System.Int64"))
{
p.SetValue(item, (long?)long.Parse(row[c].ToString()), null);
}
else
{
p.SetValue(item, row[c], null);
}
}
}
}
I am writing a Clone method using reflection. How do I detect that a property is an indexed property using reflection? For example:
public string[] Items
{
get;
set;
}
My method so far:
public static T Clone<T>(T from, List<string> propertiesToIgnore) where T : new()
{
T to = new T();
Type myType = from.GetType();
PropertyInfo[] myProperties = myType.GetProperties();
for (int i = 0; i < myProperties.Length; i++)
{
if (myProperties[i].CanWrite && !propertiesToIgnore.Contains(myProperties[i].Name))
{
myProperties[i].SetValue(to,myProperties[i].GetValue(from,null),null);
}
}
return to;
}
if (propertyInfo.GetIndexParameters().Length > 0)
{
// Property is an indexer
}
Sorry, but
public string[] Items { get; set; }
is not an indexed property, it's merely of an array type!
However the following is:
public string this[int index]
{
get { ... }
set { ... }
}
What you want is the GetIndexParameters() method. If the array that it returns has more than 0 items, that means it's an indexed property.
See the MSDN documentation for more details.
If you call property.GetValue(obj,null), and the property IS indexed, then you will get a parameter count mismatch exception. Better to check whether the property is indexed using GetIndexParameters() and then decide what to do.
Here is some code that worked for me:
foreach (PropertyInfo property in obj.GetType().GetProperties())
{
object value = property.GetValue(obj, null);
if (value is object[])
{
....
}
}
P.S. .GetIndexParameters().Length > 0) works for the case described in this article: http://msdn.microsoft.com/en-us/library/b05d59ty.aspx
So if you care about the property named Chars for a value of type string, use that, but it does not work for most of the arrays I was interested in, including, I am pretty sure, a string array from the original question.
You can convert the indexer to IEnumerable
public static IEnumerable<T> AsEnumerable<T>(this object o) where T : class {
var list = new List<T>();
System.Reflection.PropertyInfo indexerProperty = null;
foreach (System.Reflection.PropertyInfo pi in o.GetType().GetProperties()) {
if (pi.GetIndexParameters().Length > 0) {
indexerProperty = pi;
break;
}
}
if (indexerProperty.IsNotNull()) {
var len = o.GetPropertyValue<int>("Length");
for (int i = 0; i < len; i++) {
var item = indexerProperty.GetValue(o, new object[]{i});
if (item.IsNotNull()) {
var itemObject = item as T;
if (itemObject.IsNotNull()) {
list.Add(itemObject);
}
}
}
}
return list;
}
public static bool IsNotNull(this object o) {
return o != null;
}
public static T GetPropertyValue<T>(this object source, string property) {
if (source == null)
throw new ArgumentNullException("source");
var sourceType = source.GetType();
var sourceProperties = sourceType.GetProperties();
var properties = sourceProperties
.Where(s => s.Name.Equals(property));
if (properties.Count() == 0) {
sourceProperties = sourceType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic);
properties = sourceProperties.Where(s => s.Name.Equals(property));
}
if (properties.Count() > 0) {
var propertyValue = properties
.Select(s => s.GetValue(source, null))
.FirstOrDefault();
return propertyValue != null ? (T)propertyValue : default(T);
}
return default(T);
}