I am trying to write this method:
public static T Nullify<T>(T item, params Func<T, object> [] properties)
{
// Sets any specified properties to null, returns the object.
}
I will call it like this:
var kitten = new Kitten() { Name = "Mr Fluffykins", FurColour = "Brown" };
var anonymousKitten = Nullify(kitten, c => c.Name);
However I am unsure of how to do this. Any ideas?
Another approach is to do this (it doesn't need to be an extension method)
public static T Nullify<T>(this T item, params Expression<Func<T, object>> [] properties)
{
foreach(var property in properties)
{
var memberSelectorExpression = property.Body as MemberExpression;
if (memberSelectorExpression != null)
{
var propertyInfo = memberSelectorExpression.Member as PropertyInfo;
if (propertyInfo != null)
{
propertyInfo.SetValue(item, null, null);
}
}
}
return item;
}
Usage
item.Nullify(i => i.PropertyName, i => i.PropertyName2)
You'd need to pass a "setter method" not a "reader method" in properties.
static void Nullify<T, D>(T item, params Action<T, D>[] properties)
where D : class
{
foreach (var property in properties)
{
property(item, null);
}
}
usage:
Nullify<Kitten, string>(kitten, (c, d) => { c.Name = d; });
But this will just set the data for you. If you want a copy and then apply the properties, the items would probably have to be clonable (alternatively you can go though some hell with reflection):
static T Nullify<T, D>(T item, params Action<T, D>[] properties)
where D : class
where T : ICloneable
{
T copy = (T)item.Clone();
foreach (var property in properties)
{
property(copy, null);
}
return copy;
}
class Kitten : ICloneable
{
public string Name { get; set; }
public string FurColour { get; set; }
public object Clone()
{
return new Kitten() { Name = this.Name, FurColour = this.FurColour };
}
}
usage
var anonymousKitten = Nullify(kitten, (c, d) => { c.Name = d; });
Without modifying your method definition much:
namespace ConsoleApplication
{
public class Kitten : ISimpleClone<Kitten>
{
public string Name { get; set; }
public string FurColour { get; set; }
public int? Number { get; set; }
public Kitten SimpleClone()
{
return new Kitten { Name = this.Name, FurColour = this.FurColour, Number = this.Number };
}
}
public interface ISimpleClone<T>
{
T SimpleClone();
}
public class Program
{
public static PropertyInfo GetProperty<TObject, TProperty>(Expression<Func<TObject, TProperty>> propertyExpression)
{
MemberExpression body = propertyExpression.Body as MemberExpression;
if (body == null)
{
var unaryExp = propertyExpression.Body as UnaryExpression;
if (unaryExp != null)
{
body = ((UnaryExpression)unaryExp).Operand as MemberExpression;
}
}
return body.Member as PropertyInfo;
}
public static T Nullify<T>(T item, params Expression<Func<T, object>>[] properties)
where T : ISimpleClone<T>
{
// Creates a new instance
var newInstance = item.SimpleClone();
// Gets the properties that will be null
var propToNull = properties.Select(z => GetProperty<T, object>(z));
var filteredProp = propToNull
.Where(z => !z.PropertyType.IsValueType || Nullable.GetUnderlyingType(z.PropertyType) != null) // Can be null
.Where(z => z.GetSetMethod(false) != null && z.CanWrite); // Can be set
foreach (var prop in filteredProp)
{
prop.SetValue(newInstance, null);
}
return newInstance;
}
public static void Main(string[] args)
{
var kitten = new Kitten() { Name = "Mr Fluffykins", FurColour = "Brown", Number = 12 };
var anonymousKitten = Nullify(kitten, c => c.Name, c => c.Number);
Console.Read();
}
}
}
Looks a bit hacky though....
Related
I am using this answer Difference between two lists to find a difference between two lists using Linq Except. However, my objects in the lists are of generic type, so I wrote a Comparer for the generic types, then I pass it to Except method. However I receive the following error message:
Error CS1929 'List' does not contain a definition for 'Except' and the best extension method overload 'ParallelEnumerable.Except(ParallelQuery, ParallelQuery, IEqualityComparer)' requires a receiver of type 'ParallelQuery'
My call:
var differenceList = list1.Except(list2, new PropertyComparer<Guid>("ObjectId"));
And my comparer:
public class PropertyComparer<T> : IEqualityComparer<T>
{
private PropertyInfo _PropertyInfo;
public PropertyComparer(string propertyName)
{
_PropertyInfo = typeof(T).GetProperty(propertyName,
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
if (_PropertyInfo == null)
{
throw new ArgumentException(
string.Format("{0} is not a property of type {1}.", propertyName, typeof(T)));
}
}
public int GetHashCode(T obj)
{
object propertyValue = _PropertyInfo.GetValue(obj, null);
if (obj == null) return 0;
return propertyValue.GetHashCode();
}
public bool Equals(T x, T y)
{
object xValue = _PropertyInfo.GetValue(x, null);
object yValue = _PropertyInfo.GetValue(y, null);
if (xValue == null)
{
return yValue == null;
}
return xValue.Equals(yValue);
}
}
And from the declaration of Except method in System.Linq, which fits my comparer:
public static IEnumerable<TSource> Except<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer
);
What am I doing wrong and how can I get it working?
UPDATE
list1 and list2 types:
List<Contact>
Where Contact is
public class Contact : EntityBase
{
public Guid ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Email> Emails {get; set;}
}
EntityBase is
public class EntityBase : IEntity
{
public IQueryable<T> GetDBEntities<T>(ApplicationDbContext db) where T : class
{
return db.Set<T>();
}
public List<T> GetLocalEntities<T>() where T : class
{
var localProperties = this.GetType().GetProperties();
foreach (var localProperty in localProperties)
{
var localPropertyValue = localProperty.GetValue(this);
if (localPropertyValue != null && localPropertyValue.IsGenericList() == true)
{
var localPropertyValueType = localPropertyValue.GetType(); // List<object>
var localPropertyValueTypeDecoupled = localPropertyValueType.GetGenericArguments().Single(); // List<T>
if (localPropertyValueTypeDecoupled == typeof(T))
{
return (List<T>)localPropertyValue;
}
}
}
throw new Exception("Entity Types Validation Error");
}
public void ProcessEntityReference<T>(ApplicationDbContext db) where T : class
{
// T is Email
var remoteList = this.GetDBEntities<T>(db).ToList();
var updatedList = GetLocalEntities<T>();
var toBeAdded = updatedList.Except(remoteList, new PropertyComparer<Guid>("ContactId"));
var toBeDeleted = new List<object>();
throw new NotImplementedException();
}
public void ProcessEntityReferences(ApplicationDbContext db)
{
this.ProcessEntityReference<Email>(db);
}
}
You should pass the same type used by your list to the Except method, in your example, you are using Guid but it should be of type Contact, also, your Contact class doesn't have a property called "ObjectId", try changing that for "ContactId", the following seems to work fine:
static void Main(string[] args)
{
var list1 = new List<Contact>();
list1.Add(new Contact() { ContactId = Guid.Parse("FB58F102-0CE4-4914-ABFF-ABBD3895D719") });
list1.Add(new Contact() { ContactId = Guid.Parse("5A201238-6036-4385-B848-DEE598A3520C") });
var list2 = new List<Contact>();
list2.Add(new Contact() { ContactId = Guid.Parse("FB58F102-0CE4-4914-ABFF-ABBD3895D719") });
var list3 = list1.Except(list2, new PropertyComparer<Contact>("ContactId"));
foreach (var item in list3)
Console.WriteLine(item.ContactId.ToString());
Console.ReadLine();
}
public class Contact
{
public Guid ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class PropertyComparer<T> : IEqualityComparer<T>
{
private PropertyInfo _PropertyInfo;
public PropertyComparer(string propertyName)
{
_PropertyInfo = typeof(T).GetProperty(propertyName,
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
if (_PropertyInfo == null)
{
throw new ArgumentException(
string.Format("{0} is not a property of type {1}.", propertyName, typeof(T)));
}
}
public int GetHashCode(T obj)
{
object propertyValue = _PropertyInfo.GetValue(obj, null);
if (obj == null) return 0;
return propertyValue.GetHashCode();
}
public bool Equals(T x, T y)
{
object xValue = _PropertyInfo.GetValue(x, null);
object yValue = _PropertyInfo.GetValue(y, null);
if (xValue == null)
{
return yValue == null;
}
return xValue.Equals(yValue);
}
}
Output:
5a201238-6036-4385-b848-dee598a3520c
I have the following class Franchise:
public class Franchise
{
public string FolderName { get; set; }
public string InstallerExeName { get; set; }
}
I have a method that checks specific property value for uniqness among all franchises in the db.
public bool ValidateFolderName(string folderName)
{
var allFranchises = _franchiseService.GetAll();
var result = allFranchises.Any(f => f.FolderName == folderName);
return result;
}
The problem is I have to check another property for uniqness:
public bool ValidateInstallerExeName(string installerExeName)
{
var allFranchises = _franchiseService.GetAll();
var result = allFranchises.Any(f => f.InstallerExeName == installerExeName);
return result;
}
I want to avoid code duplication by making a generic method. Something like:
public bool ValidateProperty(string propertyName)
{
var allFranchises = _franchiseService.GetAll();
// Not sure how to write this line
var result = allFranchises.Any(f => f.[propertyName] == propertyName);
return result;
}
The problem is I am not sure how to re-write this line of code so that it can get the property name and check its value by the provided parameter:
var result = allFranchises.Any(f => f.[propertyName] == propertyName);
I know I can do something like this with reflection:
franchise.GetType().GetProperty(propertyName).GetValue(franchise, null);
but I am not sure how can I make this to fit my case. Any help with working example will be greatly appreciated. Thanks!
Here is a full working example using reflection:
class Program
{
private static List<Franchise> allFranchises;
static void Main(string[] args)
{
allFranchises = new List<Franchise>
{
new Franchise() { FolderName=#"c:\1", InstallerExeName="1.exe" },
new Franchise() { FolderName=#"c:\2", InstallerExeName="2.exe" },
new Franchise() { FolderName=#"c:\3", InstallerExeName="3.exe" },
new Franchise() { FolderName=#"c:\4", InstallerExeName="4.exe" },
new Franchise() { FolderName=#"c:\5", InstallerExeName="5.exe" },
};
Console.WriteLine(ValidateProperty("FolderName", #"c:\2", allFranchises));
Console.WriteLine(ValidateProperty("InstallerExeName", "5.exe", allFranchises));
Console.WriteLine(ValidateProperty("FolderName", #"c:\7", allFranchises));
Console.WriteLine(ValidateProperty("InstallerExeName", "12.exe", allFranchises));
}
public static bool ValidateProperty(string propertyName, object propertyValue, IEnumerable<Franchise> validateAgainst)
{
PropertyInfo propertyInfo = typeof(Franchise).GetProperty(propertyName);
return validateAgainst.Any(f => propertyInfo.GetValue(f, null) == propertyValue);
}
}
public class Franchise
{
public string FolderName { get; set; }
public string InstallerExeName { get; set; }
}
It will print out:
True
True
False
False
as expected.
public bool ValidateProperty<TType, TPropertyType>(Func<TType, TPropertyType> propertySelector, TPropertyType propertyValue)
{
return _franchiseService.GetAll().Any(f => propertySelector(f) == propertyValue);
}
You can call it like this:
if( ValidateProperty(x => x.FirstName, "Joe") )
This does not use reflection and you have intellisense for your propertyname as well.
You may use an extension method:
public static bool ValidateProperty(
this IEnumerable<Franchise> franchises,
string property,
object value)
{
var prop = typeof(Franchise).GetProperty(property);
if (prop == null)
throw new ArgumentException("Property does not exist");
return franchises.Any(f =>
prop.GetValue(f) == value);
}
Use it like this:
var r = _franchiseService.GetAll().ValidateProperty("FolderName", "myfolder1");
You can build the function you want using System.Linq.Expressions
public class Franchise
{
public string FolderName { get; set; }
public string InstallerExeName { get; set; }
bool ValidateProperty(string propertyName) {
var allFranchises = new List<Franchise>();
var parameter = Expression.Parameter(typeof(Franchise));
var property = Expression.Property(parameter, propertyName);
var thisExpression = Expression.Constant(this, typeof(Franchise));
var value = Expression.Property(thisExpression, propertyName);
var notThis = Expression.ReferenceNotEqual(thisExpression, property);
var equal = Expression.Equal(value, property);
var lambda = Expression.Lambda<Func<Franchise, bool>>(Expression.And(notThis, equal));
// lamda is now the equivalent of:
// x => !object.ReferenceEquals(this, x) && object.Equals(x.Property, this.Property)
return allFranchises.Any(lambda.Compile());
}
}
If allFranchises is of type IQueryable you use allFranchises.Any(lambda)
You could also caches the expression for later use if you are worried about performance.
}
I want to get back the default values of all properties in a range of objects. The logic used is that if all of the property values in the range are the same, then use that for the default value, otherwise leave it null/type default.
I'm not sure if there is a better way to do this, but I'm open to all suggestions. I have created a working solution that is fairly generic, but I want it to be more so if possible. The current problem is that I have to have the if/elseif chain of the same code with a single difference of explicitly defining the type. I couldn't figure out how to get back the GetValue of the PropertyInfo and have the type properly pass into the generic functions. Once I got the object back, it would always pass down into the Generic as 'object' instead of 'int','decimal', etc. I also ran into the boxing/unboxing issue with nullables. I tried setting up the GetPropertyValue function with a generic return, but that requires you to pass in the type, which I'm not doing since I get it inside the function.
All of this code is just a working example. My classes have hundreds of properties and with 30 different classes, thats around 3000 properties I don't want to explicitly write out.
public class MainFunction
{
public MainFunction()
{
ParentClass defaultClass = new ParentClass();
List<ParentClass> results = MyDatabaseCallThatGetsBackListOfClass();
defaultClass = Generic.GetDefaultProperty(defaultClass, results);
}
private List<ParentClass> MyDatabaseCallThatGetsBackListOfClass()
{
List<ParentClass> populateResults = new List<ParentClass>();
for (int i = 0; i < 50; i++)
{
populateResults.Add(new ParentClass()
{
Class1 = new SubClass1()
{
Property1 = "Testing",
Property2 = DateTime.Now.Date,
Property3 = true,
Property4 = (decimal?)1.14,
Property5 = (i == 1 ? 5 : 25), // different, so should return null
Class1 = new SubSubClass1()
{
Property1 = "Test"
},
Class2 = new SubSubClass2()
},
Class2 = new SubClass2()
{
Property1 = null,
Property2 = 10,
Property3 = (i == 1 ? 15 : 30), // different, so should return null
Property4 = 20
}
});
}
return populateResults;
}
}
public class ParentClass
{
public ParentClass()
{
this.Class1 = new SubClass1();
this.Class2 = new SubClass2();
}
public SubClass1 Class1 { get; set; }
public SubClass2 Class2 { get; set; }
}
public class SubClass1
{
public SubClass1()
{
this.Class1 = new SubSubClass1();
this.Class2 = new SubSubClass2();
}
public string Property1 { get; set; }
public DateTime? Property2 { get; set; }
public bool? Property3 { get; set; }
public decimal? Property4 { get; set; }
public int? Property5 { get; set; }
public bool Property6 { get; set; }
public decimal Property7 { get; set; }
public DateTime Property8 { get; set; }
public int Property9 { get; set; }
public SubSubClass1 Class1 { get; set; }
public SubSubClass2 Class2 { get; set; }
}
public class SubClass2
{
public int? Property1 { get; set; }
public int? Property2 { get; set; }
public int Property3 { get; set; }
public int Property4 { get; set; }
}
public class SubSubClass1
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class SubSubClass2
{
public decimal? Property1 { get; set; }
public decimal Property2 { get; set; }
}
public static class Generic
{
public static T GetDefaultProperty<T>(T defaultItem, List<T> itemList)
where T : class
{
Type defaultType = defaultItem.GetType();
var props = defaultType.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead);
foreach (var p in props)
{
if (p.PropertyType.IsClass && p.PropertyType != typeof(string))
{
dynamic classProperty = GetPropertyValue(defaultItem, p.Name);
var classList = GetClassSubList(itemList, classProperty, p.Name);
p.SetValue(defaultItem, GetDefaultProperty(classProperty, classList), null);
}
else
{
if (p.PropertyType == typeof(int?))
{
List<int?> subList = GetPropertySubList(itemList, TypeDefault<int?>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(bool?))
{
List<bool?> subList = GetPropertySubList(itemList, TypeDefault<bool?>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(decimal?))
{
List<decimal?> subList = GetPropertySubList(itemList, TypeDefault<decimal?>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(DateTime?))
{
List<DateTime?> subList = GetPropertySubList(itemList, TypeDefault<DateTime?>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(string))
{
List<string> subList = GetPropertySubList(itemList, TypeDefault<string>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(int))
{
List<int> subList = GetPropertySubList(itemList, TypeDefault<int>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(bool))
{
List<bool> subList = GetPropertySubList(itemList, TypeDefault<bool>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(decimal))
{
List<decimal> subList = GetPropertySubList(itemList, TypeDefault<decimal>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
else if (p.PropertyType == typeof(DateTime))
{
List<DateTime> subList = GetPropertySubList(itemList, TypeDefault<DateTime>(), p.Name);
if (subList.Distinct().ToList().Count == 1)
{
p.SetValue(defaultItem, subList.FirstOrDefault(), null);
}
}
}
}
return defaultItem;
}
private static object GetPropertyValue<T>(T item, string propertyName)
{
if (item == null || string.IsNullOrEmpty(propertyName))
{
return null;
}
PropertyInfo pi = item.GetType().GetProperty(propertyName);
if (pi == null)
{
return null;
}
if (!pi.CanRead)
{
return null;
}
return pi.GetValue(item, null);
}
private static List<TReturn> GetClassSubList<T, TReturn>(List<T> list, TReturn returnType, string propertyName)
where T : class
where TReturn : class
{
return list.Select(GetClassSelection<T, TReturn>(propertyName)).ToList();
}
private static Func<T, TReturn> GetClassSelection<T, TReturn>(string fieldName)
where T : class
where TReturn : class
{
ParameterExpression p = Expression.Parameter(typeof(T), "t");
var body = Expression.Property(p, fieldName);
return Expression.Lambda<Func<T, TReturn>>(body, new ParameterExpression[] { p }).Compile();
}
private static List<TReturn> GetPropertySubList<T, TReturn>(List<T> list, TReturn returnType, string propertyName)
where T : class
{
return list.Select(GetPropertySelection<T, TReturn>(propertyName)).ToList();
}
private static Func<T, TReturn> GetPropertySelection<T, TReturn>(string fieldName)
where T : class
{
ParameterExpression p = Expression.Parameter(typeof(T), "t");
var body = Expression.Property(p, fieldName);
return Expression.Lambda<Func<T, TReturn>>(body, new ParameterExpression[] { p }).Compile();
}
private static T TypeDefault<T>()
{
return default(T);
}
You can switch the huge IF statement block with this:
var result = itemList.Select(x => p.GetValue(x, null)).Distinct();
if (result.Count() == 1)
{
p.SetValue(defaultItem, result.First(), null);
}
If you use Distinct(), references/value types are compared using object.Equals that first tests reference equality, and later actual values. This method has only one draw-back: boxing/unboxing. Use that code for reference types.
Note: there's already a lot of boxing happening in you code.
Reflection is based on a "object", so it's pretty hard not to have boxing issues.
For example:
Type defaultType = defaultItem.GetType(); // boxing on value types.
p.SetValue(defaultItem, subList.FirstOrDefault(), null); // boxing
Boxing is minor cost with reflection. You can run benchmarks to check.
As for your actual problem; you have list of objects and you want to compare them all, recursively. If there is no difference between two objects, you want to set the property in defaultItem to a property value that all objects share.
Ignoring the reason for whatever reason you're doing this(since I don't care; rather the solution to this problem is interesting), let's continue :P
Your best bet is to generate strongly-typed comparer on startup using reflection. Generate the code using StringBuilder and later use CSharpCodeProvider() to compile from StringBuilder and return strongly-typed delegate that has no reflection overhead. This is the fastest one I can think of, right now. The only hit it will take is the first interrogation of reflection metadata on startup. It's only per T.
On production code, you can cache that strongly typed comparer onto DLL, so the hit will be only one-time event.
private static class StrongClassComparer<T>
{
public static Func<T, string> GenerateMethod()
{
var code = new StringBuilder();
// generate your STRONGLY-TYPED code here.
// into code variable.
code.Append("public class YourClassInCode { "+
" public static string YourClassStaticMethod("+ typeof(T).Name+ " test)" +
" { return string.Empty; } }");
var compiler = new CSharpCodeProvider();
var parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add(typeof (T).Assembly.Location);
parameters.CompilerOptions = "/optimize + ";
var results = compiler.CompileAssemblyFromSource(parameters, code.ToString());
var #class = results.CompiledAssembly.GetType("YourClassInCode");
var method = #class.GetMethod("YourClassStaticMethod");
return (Func<T, string>) Delegate.CreateDelegate(typeof (Func<T, string>), method);
}
}
Is there a way to get the value of an annotation in server side code? For example, I have:
public class Dummy
{
[Display(Name = "Foo")]
public string foo { get; set; }
[Display(Name = "Bar")]
public string bar { get; set; }
}
I want to be able to get the value "Foo" on server side with out posting it back to the page, but like an attribute of the class, or something of the sort. Like a #Html.LabelFor(model => model.Foo) But in c# server code.
Is that possible?
Thank you.
Something like this?
string displayName = GetDisplayName((Dummy x) => x.foo);
// ...
public static string GetDisplayName<T, U>(Expression<Func<T, U>> exp)
{
var me = exp.Body as MemberExpression;
if (me == null)
throw new ArgumentException("Must be a MemberExpression.", "exp");
var attr = me.Member
.GetCustomAttributes(typeof(DisplayAttribute), false)
.Cast<DisplayAttribute>()
.SingleOrDefault();
return (attr != null) ? attr.Name : me.Member.Name;
}
Or, if you want to be able to call the method against an instance and take advantage of type inference:
var dummy = new Dummy();
string displayName = dummy.GetDisplayName(x => x.foo);
// ...
public static string GetDisplayName<T, U>(this T src, Expression<Func<T, U>> exp)
{
var me = exp.Body as MemberExpression;
if (me == null)
throw new ArgumentException("Must be a MemberExpression.", "exp");
var attr = me.Member
.GetCustomAttributes(typeof(DisplayAttribute), false)
.Cast<DisplayAttribute>()
.SingleOrDefault();
return (attr != null) ? attr.Name : me.Member.Name;
}
You will need to use reflection. Here is a sample console program that does what you want.
class Program
{
static void Main(string[] args)
{
Dummy dummy = new Dummy();
PropertyInfo[] properties = dummy.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
IEnumerable<DisplayAttribute> displayAttributes = property.GetCustomAttributes(typeof(DisplayAttribute), false).Cast<DisplayAttribute>();
foreach (DisplayAttribute displayAttribute in displayAttributes)
{
Console.WriteLine("Property {0} has display name {1}", property.Name, displayAttribute.Name);
}
}
Console.ReadLine();
}
}
public class Dummy
{
[Display(Name = "Foo")]
public string foo { get; set; }
[Display(Name = "Bar")]
public string bar { get; set; }
}
This would produce the following result:
http://www.codetunnel.com/content/images/reflectresult.jpg
I am not sure if this is possible but I want to iterate through a class and set a field member property without referring to the field object explicitly:
public class Employee
{
public Person _person = new Person();
public void DynamicallySetPersonProperty()
{
MemberInfo[] members = this.GetType().GetMembers();
foreach (MemberInfo member in members.Where(a => a.Name == "_person"))
//get the _person field
{
Type type = member.GetType();
PropertyInfo prop = type.GetProperty("Name"); //good, this works, now to set a value for it
//this line does not work - the error is "property set method not found"
prop.SetValue(member, "new name", null);
}
}
}
public class Person
{
public string Name { get; set; }
}
In the answer that I marked as the answer you need to add:
public static bool IsNullOrEmpty(this string source)
{
return (source == null || source.Length > 0) ? true : false;
}
Here's a complete working example:
public class Person
{
public string Name { get; set; }
}
class Program
{
static void PropertySet(object p, string propName, object value)
{
Type t = p.GetType();
PropertyInfo info = t.GetProperty(propName);
if (info == null)
return;
if (!info.CanWrite)
return;
info.SetValue(p, value, null);
}
static void PropertySetLooping(object p, string propName, object value)
{
Type t = p.GetType();
foreach (PropertyInfo info in t.GetProperties())
{
if (info.Name == propName && info.CanWrite)
{
info.SetValue(p, value, null);
}
}
}
static void Main(string[] args)
{
Person p = new Person();
PropertySet(p, "Name", "Michael Ellis");
Console.WriteLine(p.Name);
PropertySetLooping(p, "Name", "Nigel Mellish");
Console.WriteLine(p.Name);
}
}
EDIT: added a looping variant so you could see how to loop through property info objects.
public class Person
{
public string Name { get; set; }
}
public class Employee
{
public Person person = new Person();
public void DynamicallySetPersonProperty()
{
var p = GetType().GetField("person").GetValue(this);
p.GetType().GetProperty("Name").SetValue(p, "new name", null);
}
}
With the following Extension methods that I have created, you can set or get any property value even if they are nested
GetPropertyValue(customObject, "Property.Nested.Child.Name");
or set
SetPropertyValue(customObject, "Property.Nested.Child.Name", "my custom name");
private class TargetProperty
{
public object Target { get; set; }
public PropertyInfo Property { get; set; }
public bool IsValid { get { return Target != null && Property != null; } }
}
private static TargetProperty GetTargetProperty(object source, string propertyName)
{
if (!propertyName.Contains("."))
return new TargetProperty { Target = source, Property = source.GetType().GetProperty(propertyName) };
string[] propertyPath = propertyName.Split('.');
var targetProperty = new TargetProperty();
targetProperty.Target = source;
targetProperty.Property = source.GetType().GetProperty(propertyPath[0]);
for (int propertyIndex = 1; propertyIndex < propertyPath.Length; propertyIndex++)
{
propertyName = propertyPath[propertyIndex];
if (!string.IsNullOrEmpty(propertyName))
{
targetProperty.Target = targetProperty.Property.GetValue(targetProperty.Target, null);
targetProperty.Property = targetProperty.Target.GetType().GetProperty(propertyName);
}
}
return targetProperty;
}
public static bool HasProperty(this object source, string propertyName)
{
return GetTargetProperty(source, propertyName).Property != null;
}
public static object GetPropertyValue(this object source, string propertyName)
{
var targetProperty = GetTargetProperty(source, propertyName);
if (targetProperty.IsValid)
{
return targetProperty.Property.GetValue(targetProperty.Target, null);
}
return null;
}
public static void SetPropertyValue(this object source, string propertyName, object value)
{
var targetProperty = GetTargetProperty(source, propertyName);
if(targetProperty.IsValid)
{
targetProperty.Property.SetValue(targetProperty.Target, value, null);
}
}
And here are a couple of tests for it
[TestFixture]
public class ObjectExtensionsTest
{
private class MockClass
{
public MockClass()
{
Nested = new NestedMockClass();
}
public string Id { get; set; }
public string Name { get; set; }
public string GetOnly { get { return "MockClass"; } }
public string SetOnly { set { } }
public NestedMockClass Nested { get; set; }
}
private class NestedMockClass
{
public string NestedId { get; set; }
public string NestedName { get; set; }
public string NestedGetOnly { get { return "NestedMockClass"; } }
public string NestedSetOnly { set { } }
}
[Test]
public void TestShouldFindProperty()
{
MockClass mockObject = new MockClass();
Assert.IsTrue(mockObject.HasProperty("Id"));
Assert.IsTrue(mockObject.HasProperty("Name"));
Assert.IsTrue(mockObject.HasProperty("GetOnly"));
Assert.IsTrue(mockObject.HasProperty("SetOnly"));
Assert.IsTrue(mockObject.HasProperty("Nested"));
Assert.IsTrue(mockObject.HasProperty("Nested.NestedId"));
Assert.IsTrue(mockObject.HasProperty("Nested.NestedName"));
Assert.IsTrue(mockObject.HasProperty("Nested.NestedGetOnly"));
Assert.IsTrue(mockObject.HasProperty("Nested.NestedSetOnly"));
}
[Test]
public void TestShouldGetPropertyValue()
{
MockClass mockObject = new MockClass();
mockObject.Id = "1";
mockObject.Name = "Name";
mockObject.Nested.NestedId = "NestedId";
mockObject.Nested.NestedName = "NestedName";
Assert.AreEqual(mockObject.Id, mockObject.GetPropertyValue("Id"));
Assert.AreEqual(mockObject.Name, mockObject.GetPropertyValue("Name"));
Assert.AreEqual(mockObject.GetOnly, mockObject.GetPropertyValue("GetOnly"));
Assert.AreEqual(mockObject.Nested.NestedId, mockObject.GetPropertyValue("Nested.NestedId"));
Assert.AreEqual(mockObject.Nested.NestedName, mockObject.GetPropertyValue("Nested.NestedName"));
}
[Test]
public void TestShouldSetPropertyValue()
{
MockClass mockObject = new MockClass();
mockObject.SetPropertyValue("Id", "1");
mockObject.SetPropertyValue("Name", "Name");
mockObject.SetPropertyValue("Nested.NestedId", "NestedId");
mockObject.SetPropertyValue("Nested.NestedName", "NestedName");
Assert.AreEqual(mockObject.Id, "1");
Assert.AreEqual(mockObject.Name, "Name");
Assert.AreEqual(mockObject.Nested.NestedId, "NestedId");
Assert.AreEqual(mockObject.Nested.NestedName, "NestedName");
}
}
Hope you find it useful.
You are trying to set the Name property of your Employee class's _person field. It doesn't have one. Try this:
prop.SetValue(((FieldInfo)member).GetValue(this), "new name", null)
Not sure if you need to cast the first argument like this:
prop.SetValue((Person)((FieldInfo)member).GetValue(this), "new name", null)
This then applies it to the value of the _person field instead.
You a trying to perform SetValue() on the property Name of the variable member that is a MemberInfo object and this proeprty is read only.
Note you do not need to iterate over all memebers and you do not need to get the field _person with reflection as it is defined in the same class as the method DynamicallySetPersonProperty().
So the code shoul read like this.
PropertyInfo property = this._person.GetType().GetProperty("Name");
property.SetValue(this._person, "new name", null);
The first line will fail if _person is null. So you can use reflectiopn to get the type of the field.
FieldInfo field = this.GetType().GetField("_person", BindingFlags.Public);
PropertyInfo property = field.FieldType.GetProperty("Name");
But now accessing this property will still fail if _personis null.
property.Setvalue(field.GetValue(this), "new name", null);
try this:
public static void ApplyPropertyChanges(this object objDest, object objToCopyFrom)
{
if (objDest == null)
throw new ArgumentNullException();
if (objToCopyFrom == null)
throw new ArgumentNullException("objToCopyFrom");
if (objDest.GetType() != objToCopyFrom.GetType())
throw new Exception("Invalid type. Required: \"" + objDest.GetType().ToString() + "\"");
foreach (System.Reflection.PropertyInfo piOrig in objDest.GetType().GetProperties())
{
object editedVal = objToCopyFrom.GetType().GetProperty(piOrig.Name).GetValue(objToCopyFrom, null);
piOrig.SetValue(objDest,
editedVal,
null);
}
}
usage example:
public ActionResult Edit(Team editedTeamData)
{
if (!ModelState.IsValid)
return View();
Team origTeam = (from t in _db.Teams
where t.TeamID == editedTeamData.TeamID
select t).FirstOrDefault();
origTeam.ApplyPropertyChanges(editedTeamData);
_db.SubmitChanges();
return RedirectToAction("Index");
}