Getting data from property attribute - c#

I have a view model which uses custom attributes such as
public int Id { get; set; }
public string Name { get; set; }
[IsEnumeration(typeof(CaseStatus))]
public string Status { get; set; }
IsEnumeration is a custom attribute which takes an Enumeration superclass as a parameter (actually it takes any type, but that doesn't matter since noone else will be using this)
public class IsEnumerationAttribute : Attribute
{
public Type Enumeration;
public IsEnumerationAttribute(Type enumeration)
{
Enumeration = enumeration;
}
}
What I want is to be able to get the type specified for any parameter. Currently my code looks like this:
public T EnumerationValuesToDisplayNames<T>(T item) where T : new()
{
if (LoggedInUser.IsSuper) return item;
var tProps = typeof (T).GetProperties()
.Where(prop => Attribute
.IsDefined(prop, typeof (IsEnumerationAttribute)));
foreach (var prop in tProps)
{
if (prop.GetValue(item, null) != null)
{
/*
Here I look through all properties with the IsEnumerable attribute.
I want to do something such as:
var type = prop.GetAttribute(item, typeof(IsEnumerable));
var displayName = Enumeration<type>.FromId(prop.GetValue(item, null));
prop.SetValue(item, displayName);
*/
}
}
return item;
}
I hope this makes sense, any help would be greatly appreciated, thanks

Assuming from your post you have a class defined as such:
public class Enumeration<T> {
public static string FromId(string id) {
// FromId Implmentation
}
}
Then you should just need
foreach (var prop in tProps) {
var id=prop.GetValue(item, null);
if (id!=null) {
var type = prop.GetCustomAttributes(typeof(EnumerationAttribute>,true).OfType<EnumerationAttribute>().Select(x=>x.Enumeration).First();
var enumerationType=typeof(Enumeration<>).MakeGenericType(type);
var fromIdMethod=enumerationType.GetMethod("FromId",BindingFlags.Public|BindingFlags.Static|BindingFlags.InvokeMethod);
var displayName=fromIdMethod.Invoke(null,new object[] {id});
prop.SetValue(item, displayName);
}
}
Alternatively you could implement the FromId method directly in the EnumerationAttribute then you could just call it directly like so...
foreach (var prop in tProps) {
var id=prop.GetValue(item, null);
if (id!=null) {
var enumAttrib = prop.GetCustomAttributes(typeof(EnumerationAttribute>,true).OfType<EnumerationAttribute>().First();
var displayName=enumAttrib.FromId((string)id);
prop.SetValue(item, displayName);
}

Related

how can I parse object into a class like json deserialize?

I want to make a own parser that can parse object values into the <T> Type that contains class with propertys.
the class ASObject is just a Dictionary<string, object>
public class Example {
public string User { get; set; }
public int Id { get; set; }
}
public static class ServiceResultParser<T>
{
public static T Parse(ASObject AS)
{
foreach(var l in AS.Values)
{
}
}
}
Usage:
var Result = ServiceResultParser.Parse<Example>(theobject);
string User = Result.User;
that is only a test class that I called Example
in json we can use JsonConvert.DeserializeObject<T>(value)
and no I dont want parse json.
how can I now parse the value into the Example class?
regarding.
You could check wheter T has a property with a name that matches the Dictionary's key:
public static class ServiceResultParser<T> where T : new()
{
public static T Parse(ASObject AS)
{
var temp = GetObject();
foreach(var l in AS)
{
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (PropertyInfo property in properties)
{
if(property.Name == l.Key) property.SetValue(temp, l.Value);
}
}
return temp;
}
protected T GetObject()
{
return new T();
}
}
You should also check if the properties type match, etc...

MetadataType and Attribute.IsDefined

I am using a database first approach and have a class SalesWallboard mapped to the database table.
SalesWallboard.cs:
namespace Wallboards.DAL.SchnellDb
{
public partial class SalesWallboard
{
public int Id { get; set; }
public string StaffName { get; set; }
...
}
}
I have created a MetadataType class for this to apply custom attributes
SalesWallboardModel.cs
namespace Wallboards.DAL.SchnellDb
{
[MetadataType (typeof(SalesWallboardModel))]
public partial class SalesWallboard
{
}
public class SalesWallboardModel
{
[UpdateFromEclipse]
public string StaffName { get; set; }
...
}
}
But in my code, when I check it using Attribute.IsDefined, it always throws false.
Code
var salesWallboardColumns = typeof(SalesWallboard).GetProperties();
foreach (var column in salesWallboardColumns)
{
if (Attribute.IsDefined(column, typeof(UpdateFromEclipse))) //returns false
{
...
}
}
I have checked the answer given here. But I don't understand the example.
Can someone please simplify this for me?
I would like to thank #elgonzo and #thehennyy for their comments and correcting my understanding with reflection.
I found what I was looking for here. But I had to make a few modifications shown below.
I created a method PropertyHasAttribute
private static bool PropertyHasAttribute<T>(string propertyName, Type attributeType)
{
MetadataTypeAttribute att = (MetadataTypeAttribute)Attribute.GetCustomAttribute(typeof(T), typeof(MetadataTypeAttribute));
if (att != null)
{
foreach (var prop in GetType(att.MetadataClassType.UnderlyingSystemType.FullName).GetProperties())
{
if (propertyName.ToLower() == prop.Name.ToLower() && Attribute.IsDefined(prop, attributeType))
return true;
}
}
return false;
}
I also got help from here because my type was in a different assembly.
Method GetType
public static Type GetType(string typeName)
{
var type = Type.GetType(typeName);
if (type != null) return type;
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
type = a.GetType(typeName);
if (type != null)
return type;
}
return null;
}
Then used it in my code like so
Code
var salesWallboardColumns = typeof(SalesWallboard).GetProperties();
foreach (var column in salesWallboardColumns)
{
var columnName = column.Name;
if (PropertyHasAttribute<SalesWallboard>(columnName, typeof(UpdateFromEclipse)))
{
...
}
}

Add to a collection of unknown type using reflection in c#

So I am using reflection to loop through the properties of one object and populating the values on a different object with properties of the same name. This works great but the problem comes when the property type is a collection. I want to be able to loop through each of the objects in the source collection and populate the same list with objects in the source collection.
public class SourceMessage
{
public string Name { get; set; }
public int Version { get; set; }
public IList<ValueDefinition> Values { get; set; }
}
public class ValueDefinition
{
public string Name { get; set; }
public string Value { get; set; }
}
public class TargetObject
{
public TargetObject()
{
Values = new List<TargetValueDefinition>();
}
public string Name { get; set; }
public int Version { get; set; }
public IList<TargetValueDefinition> Values { get; set; }
}
public class TargetValueDefinition
{
public string Name { get; set; }
public string Value { get; set; }
}
Then I use Reflection to populate the target from the source.
public static void PopulateFromMessage<T, TS>(ref T targetEntity, TS message)
{
var sourceType = typeof(TS);
var targetType = typeof(T);
foreach (var targetPropInfo in targetType.GetProperties())
{
if (sourceType.GetProperty(targetPropInfo.Name) != null)
{
var obj = sourceType.GetProperty(targetPropInfo.Name);
if (obj.PropertyType.Namespace == "System.Collections.Generic")
{
//var x = targetType.GetProperty(targetPropInfo.Name);
//PopulateFromMessage(ref x, sourceType.GetProperty(targetPropInfo.Name));
continue;
}
targetPropInfo.SetValue(targetEntity, sourceType.GetProperty(targetPropInfo.Name).GetValue(message), null);
}
}
}
So calling this would be like this:
private void DenormalizeMessage(SourceMessage message)
{
var newTargetObject = new TargetObject();
PopulateFromMessage(ref newTargetObject , message);
}
I can identify when the property is a collection but am uncertain of how to create new TargetValueDefinitions and populate them with the values from ValueDefinitions. In the end it is pretty much a copy of the SourceMessage in the form of a TargetObject.
This all stems from receiving messages and transforming them into objects with the same property names.
If your problem is iterating through items contained inside a single property when it is a collection, then the key would be to read the property value into a dynamic variable and not an object variable that is by default, this way you could use a foreach for it.
dynamic propVal = inputProperty.GetValue(item);
foreach (var subItem in propVal)
{
//do your stuff
}
Disclaimer: This is extremely unsafe to do and makes a lot of assumptions but it should puth you on the right path.
Change you method to this:
public static void PopulateFromMessage<T, TS>(T targetEntity, TS message)
{
var sourceType = typeof (TS);
var targetType = typeof (T);
foreach (var targetPropInfo in targetType.GetProperties())
{
if (targetPropInfo.PropertyType.IsGenericType)
{
if (targetPropInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>))
{
var originalList = sourceType.GetProperty(targetPropInfo.Name).GetValue(message) as IList;
if (originalList != null)
{
var argumentType = targetPropInfo.PropertyType.GetGenericArguments();
var listType = typeof (List<>);
var concreteType = listType.MakeGenericType(argumentType);
var newList = Activator.CreateInstance(concreteType) as IList;
foreach (var original in originalList)
{
var targetValue = Activator.CreateInstance(argumentType[0]);
// do this yourself. Here we're converting ValueDefinition to TargetValueDefinition
// targetValue.Fill(original);
}
targetPropInfo.SetValue(targetEntity, newList);
}
}
}
else
{
if (sourceType.GetProperty(targetPropInfo.Name) != null)
{
var obj = sourceType.GetProperty(targetPropInfo.Name);
if (obj.PropertyType.Namespace == "System.Collections.Generic")
{
//var x = targetType.GetProperty(targetPropInfo.Name);
//PopulateFromMessage(ref x, sourceType.GetProperty(targetPropInfo.Name));
continue;
}
targetPropInfo.SetValue(targetEntity, sourceType.GetProperty(targetPropInfo.Name).GetValue(message), null);
}
}
}
}
You should create a interface for each class (implement the methods and properties on interface) and implement it in each class. After, in function PopulateFromMessage should specify the interface allowed in method, with this you can use directly the properties of class with T and TS generic types.

Detecting attributes on a class to infer property?

I am not too familiar with reflection, however, would it be possible to implement a method that will return an object if that class has a property associated with a certain attribute?
I thought it might make this following implementation not being required
public interface IEntity
{
object ID { get; }
}
public class Person : IEntity
{
[Key]
public int PersonID { get; }
public string Name { get; set; }
public int Age { get; set; }
object IEntity.ID
{
get { return PersonID; }
}
}
So instead of implementing 'IEntity' for every class, you can just do something like this:
public abstract class EntityBase
{
public object ID { get { return FindPrimaryKey(); } }
protected object FindPrimaryKey()
{
object key = null;
try
{
//Reflection magic
}
catch (Exception) { }
return key;
}
}
This would just save some time instead of having to go through all code-first generated classes and implementing this small feature.
Yes, that can definitely be done. Consider the following code:
protected object FindPrimaryKey()
{
object key = null;
var prop = this.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(Key)))
if (prop != null) { key = prop.GetValue(this); }
return key;
}
However, I would recommend caching that value. Add a private field for the key value:
object _keyValue;
and then set that:
protected void FindPrimaryKey()
{
var prop = this.GetType()
.GetProperties()
.Where(p => Attribute.IsDefined(p, typeof(Key)))
if (prop != null) { _keyValue = prop.GetValue(this); }
}
and then return that instead:
public object ID { get { return _keyValue; } }

Reflection exclude all attributes from base class and specific attribute from all other derived classes

I have the following base, middle and derived classes below::
public class Base
{
[DataMemberAttribute()]
public int ValueBase { get; set; }
[IgnoreForAllAttribute("Param1", "Param2")]
public int IgnoreBase { get; set; }
}
public class Middle : Base
{
[DataMemberAttribute()]
public int ValueMiddle { get; set; }
[IgnoreForAllAttribute("Param1", "Param2")]
public int IgnoreMiddle { get; set; }
}
public class MostDerived : Middle
{
[DataMemberAttribute()]
public int ValueMostDerived { get; set; }
[IgnoreForAllAttribute("Param1", "Param2")]
public int IgnoreMostDerived { get; set; }
}
I need a function that given a type, I need to return DataMemberAttribute attributes for all classes in the hierarchy except for the base.
In addition, all IgnoreForAllAttribute attributes should be ignored for all classes in the graph.
var derivedObject = new MostDerived();
var attributes = MyShinyAttributeFunction(derivedObject.GetType());
// returns [] { ValueMostDerived, ValueMiddle }
Here's a LINQ sample that assumes DateMemberAttribute and IgnoreForAllAttribute are mutually exclusive
IEnumerable<PropertyInfo> MyProperties(object o)
{
o.GetType().GetProperties()
.Where(p => !(p.DeclaringType is Base))
.Where(p => p.GetCustomAttributes(false).Any(a => a is DataMemberAttribute)
}
And a sample assuming the attributes are NOT mutually exclusive
IEnumerable<PropertyInfo> MyProperties(object o)
{
o.GetType().GetProperties()
.Where(p => !(p.DeclaringType is Base))
.Where(p =>
{
var attributes = p.GetCustomAttributes(false);
return attributes.Any(a => a is DataMemberAttribute)
&& !attributes.Any(a => a is IgnoreForAllAttribute);
}
}
var properties = new List<PropertyInfo>();
GetProps(typeof(MostDerived), properties);
GetProps is a recursive function that gets the properties for the declaring type and then calls itself for the next type in the hierarchy. It stops when it gets to 'Base'
private static void GetProps(Type T, List<PropertyInfo> Properties)
{
if (T != typeof(Base))
{
var pis = T.GetProperties();
foreach (var pi in pis)
{
if (pi.DeclaringType == T &&
pi.GetCustomAttribute<DataMemberAttribute>() != null &&
pi.GetCustomAttribute<IgnoreForAllAttribute>() == null)
{
Properties.Add(pi);
}
}
GetProps(T.BaseType, Properties);
}
}
The properties list will contain properties which have the DataMemberAttribute and don't have the IgnoreForAllAttribute.
You can use the following function to get desired results:
In propertyNames, you will get the properties you need.
Usage:
List<string> propertyNames = new List<string>();
List<string> basePropertyNames = new List<string>();
MyShinyAttributeFunction(derivedObject.GetType(), ref propertyNames, ref basePropertyNames);
Function:
void MyShinyAttributeFunction(Type type, ref List<string> propertyNames, ref List<string> basePropertyNames)
{
if (type == null)
return;
MyShinyAttributeFunction(type.BaseType, ref propertyNames, ref basePropertyNames);
foreach (var property in type.GetProperties())
{
foreach (object customAttr in property.GetCustomAttributes(false))
{
if (customAttr is DataMemberAttribute)
{
if (type.BaseType.Name.Equals("Object"))
{
DataMemberAttribute attribute = (DataMemberAttribute)customAttr;
if (!basePropertyNames.Contains(property.Name))
basePropertyNames.Add(property.Name);
}
else
{
DataMemberAttribute attribute = (DataMemberAttribute)customAttr;
if (!propertyNames.Contains(property.Name) && !basePropertyNames.Contains(property.Name))
propertyNames.Add(property.Name);
}
}
}
}
}
You must make use of the following:
Type::GetProperties method, to retrieve all properties.
MemberInfo::GetCustomAttributes to retrieve the attributes for each property.
Type::BaseType property to retrieve base type for a type.
A recursive function that stops when BaseType does not exist should do the trick.

Categories