public class Person {
string Name
string Address
int Age
.. 100+ more columns
}
var result = new List<Person>();
foreach (var item in result )
{
//loop column and trim the values.
}
I want the simplest way to loop the columns (assuming 100+ columns) where datatype is string then trim the value.
To rephrase in more C# terms: I want to update all properties and fields of an object that are of type string with trimmed value as item.StringProp = item.StringProp.Trim(). I don't want to manually write update for each property.
You could use reflection and Linq for filtering the properties of type string. From the OP, it looks like you are using Fields instead of properties. Please note it is unclear whether the Properties/Fields are public from OP, if you need to use public fields/properties, please use BindingFlags.Public
public List<T> TrimList<T>(List<T> source)
{
foreach(var property in typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance).Where(x=>x.FieldType== typeof(string)))
{
foreach(var personItem in source)
property.SetValue(personItem,Convert.ToString(property.GetValue(personItem)).Trim());
}
return source;
}
If properties, you could use
public List<T> TrimList<T>(List<T> source)
{
foreach(var property in typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance).Where(x=>x.PropertyType== typeof(string)))
{
foreach(var personItem in source)
property.SetValue(personItem,Convert.ToString(property.GetValue(personItem)).Trim());
}
return source;
}
Demo Code
Note: prior to .NET 4.5 you need to pass null as a second argument:
public List<T> TrimList<T>(List<T> source)
{
foreach(var property in typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance).Where(x=>x.PropertyType== typeof(string)))
{
foreach(var personItem in source)
property.SetValue(personItem,Convert.ToString(property.GetValue(personItem,null)).Trim());
}
return source;
}
As well as reflection, another way is to make it the responsibility of the Person class.
public class Person {
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
.. 100+ more columns
public void DoTrim()
{
this.Name = this.Name.Trim();
this.Address = this.Address.Trim();
... still need to code 100+ properties
}
}
The advantage is that you can call it like this
var result = new List<Person>();
...
for(int i=0; i < result.Count(); i++)
{
result[i].DoTrim();
}
Or you can control your data in the Person class when you set it and use local private variables.
public class Person {
private string name;
public string Name
{
get { return name; }
set { name = value.Trim(); }
}
private string address;
public string Address
{
get { return address; }
set { address= value.Trim(); }
}
....
This is how I would implement it:
class Program
{
static void Main(string[] args)
{
var obj = new Person
{
MyProperty = " A",
MyProperty1 = " A ",
MyProperty2 = "A ",
MyProperty3 = "A A A",
};
TrimStrings(obj);
}
public static void TrimStrings(object obj)
{
Type stringType = typeof(string);
var properties = obj.GetType().GetProperties().Where(x => x.PropertyType == stringType);
foreach(var property in properties)
{
string value = (string)property.GetValue(obj);
property.SetValue(obj, value?.Trim());
}
}
}
public class Person
{
public string MyProperty { get; set; }
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public string MyProperty3 { get; set; }
}
Output:
{"MyProperty":"A","MyProperty1":"A","MyProperty2":"A","MyProperty3":"A
A A"}
You can use This Nuget Package
.After Install use it as bellow:
result.ForEach(x => x.AdjustString());
Related
I have a class that I've created
public class DataRecord
{
public string PayerAccount { get; set; }
public string GlobalEntityType { get; set; }
public string GlobalEntityNumber { get; set; }
}
I am now trying to access this DataRecord in a different method through the use of a variable
public List<DataTest> CountAndFrequency(IEnumerable records, string ColumnName, int numResults)
{
foreach (DataRecord record in records)
{
record.ColumnName = record.ColumnName.ToUpper();
}
}
I am getting the error that DataRecord does not contain a definition for ColumnName, which of course makes sense. The question is, how do I combat this issue? I've been scouring the internet to no avail and would appreciate any help.
Thanks in advance!
You can use reflection for this. Try this
public static List<DataTest> CountAndFrequency(IEnumerable<DataRecord> records, string ColumnName, int numResults)
{
foreach (DataRecord record in records)
{
var prop = typeof(DataRecord).GetProperty(ColumnName);
var value = prop.GetValue(record).ToString().ToUpper();
prop.SetValue(record, value);
}
}
If you want to access data via a string name, you store the data in a Dictionary<string,string>.
public class DataRecord
{
private readonly Dictionary<string, string> data;
public DataRecord()
{
this.data = new Dictionary<string, string>();
}
// Access data with names
public string this[string columnName]
{
get{ return data[columnName]; }
set{ data[columnName] = value;}
}
// Fake properties
public string PayerAccount
{
get => data[nameof(PayerAccount)];
set => data[nameof(PayerAccount)] = value;
}
public string GlobalEntityType
{
get => data[nameof(GlobalEntityType)];
set => data[nameof(GlobalEntityType)] = value;
}
public string GlobalEntityNumber
{
get => data[nameof(GlobalEntityNumber)];
set => data[nameof(GlobalEntityNumber)] = value;
}
}
class Program
{
static void Main(string[] args)
{
var record = new DataRecord
{
PayerAccount = "10024",
GlobalEntityType = "QXT",
GlobalEntityNumber = "509382"
};
var number = record["GlobalEntityNumber"];
// 509382
}
}
I have a list where TestClass is a class with some predefined properties. So when i get data and bind my list with data i need to ignore some properties of TestClass by comparing it with a list. How can i achieve that?
Below is my code
public class TestClass
{
public int id{get;set;}
public string fname{get;set;}
public string lname{get;set;}
public string job {get;set;}
public string role{get;set;}
public string address{get;set;}
}
List<TestClass> ulist = null;
ulist = ToList<TestClass>(usersdataset.tables[0]); //fill my list with the data code is given below
so after getting the data into the list i need to remove some properties by comparing it with list of properties which should be returned.for example if my filteredlist should only show id,fname,role then i need to remove the extra properties from my ulist. so after the filter ulist should only contain id,fname and role
ToList Method
public static List<T> ToList<T>(DataTable dataTable) where T : new()
{
var dataList = new List<T>();
//Define what attributes to be read from the class
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
//Read Attribute Names and Types
var objFieldNames = typeof(T).GetProperties(flags).Cast<PropertyInfo>().
Select(item => new
{
Name = item.Name,
Type = Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType
}).ToList();
//Read Datatable column names and types
var dtlFieldNames = dataTable.Columns.Cast<DataColumn>().
Select(item => new {
Name = item.ColumnName,
Type = item.DataType
}).ToList();
foreach (DataRow dataRow in dataTable.AsEnumerable().ToList())
{
var classObj = new T();
foreach (var dtField in dtlFieldNames)
{
PropertyInfo propertyInfos = classObj.GetType().GetProperty(dtField.Name);
var field = objFieldNames.Find(x => x.Name == dtField.Name);
//var field = filteredColumns.Find(x => x.PropertyName == dtField.Name);
if (field != null)
{
if (dataRow[dtField.Name] != DBNull.Value)
propertyInfos.SetValue(classObj, dataRow[dtField.Name], null);
}
}
dataList.Add(classObj);
}
return dataList;
}
Use the overvride function Equals:
This sample compare only the id property
public class TestClass
{
public int id { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public string job { get; set; }
public string role { get; set; }
public string address { get; set; }
public override bool Equals(object obj)
{
if (obj.GetType().Name != this.GetType().Name)
{
return false;
}
TestClass testclassObject = (TestClass)obj;
if (testclassObject.id != this.id)
{
return false;
}
return true;
}
I have following two classes
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
I have an instance of Employee class as follows.
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
I have a method which gets the property value based on the property name as follows:
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
In the above method,when I try to get property value of nested class like
GetPropertyValue(employee, "employee.Child.ChildName")
or
GetPropertyValue(GetPropertyValue(employee, "Family"), "ChildName"
doesn't return any value because type.GetRuntimeProperty(part) is always null.
Is there any way to fix this problem?
You problem lies in this line:
foreach (String part in nameParts)
Because you are iterating over each part of nameParts, you are also iterating over "employee", which of course is not a valid property.
Try either this:
foreach (String part in nameParts.Skip(1))
Or calling the method like this:
GetPropertyValue(employee, "Child.ChildName")
(Notice no "employee.", because you already pass in an employee)
The problem in this case is that when you split the string employee.Child.ChildName, the "employee" is the first part. However, employee is not a property of the source i.e. Employee Class.
Try this:
public class Program
{
public static void Main()
{
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
GetPropertyValue(employee, "employee.Child.ChildName");
}
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
nameParts = nameParts.Skip(1).ToArray();
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
Here, i have skipped the first part of the string i.e. "employee". However, you can solve the problem by passing Child.ChildName
This question is around 2 years old, but I found a another working solution for you question, which is easy to understand. If you initialize the object in calling calss constructor you can use dot(.) notation to assign or read property. Example -
public class Family{
public string ChildName { get; set; }
}
public class Employee{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
public Employee(){
Child = new Family();
}
}
Employee emp = new Employee();
emp.Family.ChildName = "Nested calss attribute value";
I have some public variables that are defined as follows:
public class FieldsToMonitor
{
public int Id { get; set; }
public string Title { get; set; }
public int Rev {get; set;}
}
I now want to populate those variable with values, but the fm.[varible name] needs be same as field.Name. Here how I would populate in loop if I knew the property name ahead of time and the order of the property name:
// loop 1
fm.Id = revision.Fields[field.Name].Value;
// ... loop 2 ...
fm.Title = revision.Fields[field.Name].Value;
// ... loop 3 ...
fm.Rev = revision.Fields[field.Name].Value;
Here is what I would like to do where field.Name can be substituted with property name:
fm.ID becomes
fm.[field.Name] where field.Name == "ID"
fm.Title becomes
fm.[field.Name] where field.Name == "Title"
fm.Rev becomes
fm.[field.Name] and where field.Name == "Rev"
Is there a solution for this?
Here is more code of what I have so far:
public class FieldsToMonitor
{
public int Id { get; set; }
public string Title { get; set; }
public int Rev {get; set;}
}
static BindingList<FieldsToMonitor> FieldsToMonitorList
= new BindingList<FieldsToMonitor>();
// ...
// Loop through the work item revisions
foreach (Revision revision in wi.Revisions)
{
fm = new FieldsToMonitor();
// Get values for the work item fields for each revision
var row = dataTable.NewRow();
foreach (Field field in wi.Fields)
{
fieldNameType = field.Name;
switch (fieldNameType)
{
case "ID":
case "Title":
case "Rev":
// the following doesn't work
fm + ".[field.Name]" = revision.Fields[field.Name].Value;
fm[field.Name] = revision.Fields[field.Name].Value;
row[field.Name] = revision.Fields[field.Name].Value;
break;
}
}
}
This is all heavily dependent on how these values are being retrieved, and it is difficult to tell from your limited example if the values are strings or the correct type just boxed as an object.
That being said, the following could work (but is hardly efficient):
public static void SetValue<T>(T obj, string propertyName, object value)
{
// these should be cached if possible
Type type = typeof(T);
PropertyInfo pi = type.GetProperty(propertyName);
pi.SetValue(obj, Convert.ChangeType(value, pi.PropertyType), null);
}
Used like:
SetValue(fm, field.Name, revision.Fields[field.Name].Value);
// or SetValue<FieldsToMonitor>(fm, ...);
I am not sure I understand you correctly but here is a try
public static class MyExtensions
{
public static void SetProperty(this object obj, string propName, object value)
{
obj.GetType().GetProperty(propName).SetValue(obj, value, null);
}
}
Usage like
Form f = new Form();
f.SetProperty("Text", "Form222");
You're going to have to use reflection to accomplish that. Here's a short example:
class Foo
{
public int Num { get; set; }
}
static void Main(string[] args)
{
Foo foo = new Foo() { Num = 7 };
Console.WriteLine(typeof(Foo).GetProperty("Num").GetValue(foo, null));
}
Ok so at first I thought this was easy enough, and maybe it is and I'm just too tired - but here's what I'm trying to do. Say I have the following objects:
public class Container
{
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<Telephone> Telephones { get; set; }
}
public class Telephone
{
public string CellPhone { get; set; }
}
What I need to be able to do, is 'flatten' Containers property names in to a string (including ALL child properties AND child properties of child properties) that would look something like this:
Container.Name, Container.Addresses.AddressLine1, Container.Addresses.AddressLine2, Container.Addresses.Telephones.CellPhone
Does that make any sense? I can't seem to wrap it around my head.
I suggest you to mark all the classes, you need to grab, with custom attribute after that you could do something like this
class Program
{
static void Main(string[] args)
{
var lines = ExtractHelper.IterateProps(typeof(Container)).ToArray();
foreach (var line in lines)
Console.WriteLine(line);
Console.ReadLine();
}
}
static class ExtractHelper
{
public static IEnumerable<string> IterateProps(Type baseType)
{
return IteratePropsInner(baseType, baseType.Name);
}
private static IEnumerable<string> IteratePropsInner(Type baseType, string baseName)
{
var props = baseType.GetProperties();
foreach (var property in props)
{
var name = property.Name;
var type = ListArgumentOrSelf(property.PropertyType);
if (IsMarked(type))
foreach (var info in IteratePropsInner(type, name))
yield return string.Format("{0}.{1}", baseName, info);
else
yield return string.Format("{0}.{1}", baseName, property.Name);
}
}
static bool IsMarked(Type type)
{
return type.GetCustomAttributes(typeof(ExtractNameAttribute), true).Any();
}
public static Type ListArgumentOrSelf(Type type)
{
if (!type.IsGenericType)
return type;
if (type.GetGenericTypeDefinition() != typeof(List<>))
throw new Exception("Only List<T> are allowed");
return type.GetGenericArguments()[0];
}
}
[ExtractName]
public class Container
{
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}
[ExtractName]
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<Telephone> Telephones { get; set; }
}
[ExtractName]
public class Telephone
{
public string CellPhone { get; set; }
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = true)]
public sealed class ExtractNameAttribute : Attribute
{ }
Per my comment, you could use something like this if it will always be a generic List type that you want to link to a child type. IteratePropertiesRecursively is an iterator over the properties of the given type, that will recursively enumerate the properties of the type and all child types linked through a generic List.
protected void Test()
{
Type t = typeof(Container);
string propertyList = string.Join(",", IteratePropertiesRecursively("", t).ToArray<string>());
// do something with propertyList
}
protected IEnumerable<string> IteratePropertiesRecursively(string prefix, Type t)
{
if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith(".")) prefix += ".";
prefix += t.Name + ".";
// enumerate the properties of the type
foreach (PropertyInfo p in t.GetProperties())
{
Type pt = p.PropertyType;
// if property is a generic list
if (pt.Name == "List`1")
{
Type genericType = pt.GetGenericArguments()[0];
// then enumerate the generic subtype
foreach (string propertyName in IteratePropertiesRecursively(prefix, genericType))
{
yield return propertyName;
}
}
else
{
// otherwise enumerate the property prepended with the prefix
yield return prefix + p.Name;
}
}
}
Note: This code will not correctly handle a type that recursively includes itself as a type of one of its properties. Trying to iterate over such a type will result in a StackOverflowException, as pointed out by #Dementic (thanks!).