I am trying to build an object through an attribute on a classes property that specifies a column in a supplied data row that is the value of the property, as below:
[StoredDataValue("guid")]
public string Guid { get; protected set; }
[StoredDataValue("PrograGuid")]
public string ProgramGuid { get; protected set; }
In a Build() method on a base object, I am getting the attribute values set on these properties as
MemberInfo info = GetType();
object[] properties = info.GetCustomAttributes(true);
However, at this point I am realising the limitation in my knowledge.
For a start, I don't appear to be getting back the correct attributes.
And how do I set these properties through reflection, now that I have the attributes? Am I doing / thinking something fundamentally incorrect?
There are a couple of separate issues here
typeof(MyClass).GetCustomAttributes(bool) (or GetType().GetCustomAttributes(bool)) returns the attributes on the class itself, not the attributes on members. You will have to invoke typeof(MyClass).GetProperties() to get a list of properties in the class, and then check each of them.
Once you got the property, I think you should use Attribute.GetCustomAttribute() instead of MemberInfo.GetGustomAttributes() since you exactly know what attribute you are looking for.
Here's a little code snippet to help you start:
PropertyInfo[] properties = typeof(MyClass).GetProperties();
foreach(PropertyInfo property in properties)
{
StoredDataValueAttribute attribute =
Attribute.GetCustomAttribute(property, typeof(StoredDataValueAttribute)) as StoredDataValueAttribute;
if (attribute != null) // This property has a StoredDataValueAttribute
{
property.SetValue(instanceOfMyClass, attribute.DataValue, null); // null means no indexes
}
}
EDIT: Don't forget that Type.GetProperties() only returns public properties by default. You will have to use Type.GetProperties(BindingFlags) to get other sorts of properties as well.
Related
Struggled to come up with a decent way to ask/title this question, but will try and illustrate it as best I can.
I am working with a data structure something like this:
public Foo
{
public Bar Bar {get;set;}
}
public Bar
{
public SubTypeA TypeA {get;set;}
public SubTypeB TypeB {get;set;}
...
}
public SubTypeA
{
public int Status {get;set;}
...
}
Note that I am unable to change the data structure for this.
There are many different types in the Bar class, which all have different properties within them, but common to all of them is the property of Status.
What I need to do, is given an object of type Foo, is record the statuses for every item in the Bar object within it. Not every SubType is going to have a value every time though, some could be null.
I can sort of manage it by using a recursive function like below to loop through all the properties. It isn't ideal though I don't think as the loop could get quite large as there could be a lot of properties on each SubType.
private void GetProperties(Type classType, object instance)
{
foreach (PropertyInfo property in classType.GetProperties())
{
object value = property.GetValue(instance, null);
if (value != null)
{
if (property.Name == "Status")
{
Record(classType, value);
}
GetProperties(property.PropertyType, value);
}
}
}
Is this about the only approach that there is for such a problem?
EDIT: Going by the answer given by Selman22, I have come up with another issue wherein I am trying to create an anonymous object based on the status and name of object.
var z = instance.GetType()
.GetProperties()
.Select(x => new
{
status = x.GetValue(instance).GetType().GetProperty("status").GetValue(x, null),
name = x.Name
})
.ToList();
This is throwing an error of Object does not match target type. when trying to retrieve the value. Is this possible in a 1 liner?
Type class contains GetProperty(string name, BindingFlags method) that you can use to retrieve specific property. Instead of looping through every property use this method.
http://msdn.microsoft.com/en-us/library/system.type.getproperty(v=vs.110).aspx
// Get Type object of MyClass.
Type myType=typeof(MyClass);
// Get the PropertyInfo by passing the property name and specifying the BindingFlags.
PropertyInfo myPropInfo = myType.GetProperty("MyProperty", BindingFlags.Public | BindingFlags.Instance);
You can get all Status properties using LINQ instead of recursion:
var barInstance = typeof(Foo).GetProperty("Bar").GetValue(fooInstance);
var statusProperties = barInstance.GetType()
.GetProperties()
.Select(x => x.GetValue(barInstance).GetType().GetProperty("Status"));
I'd like to implement dynamic WebGrid Creation from Model.
The Idea is to Create a Grid from Model "Description" by annotating the Model Properties with attributes.
class Model
{
public List<Product> Products {get;set;}
}
class Product
{
[GridColumn]
public String Name {get;set;}
....
}
Then I'd like to Get By Reflection All properties marked by this Attribute.
public WebGridColumns[] ColumnsFromModel(object model)
{
// Here model is List<T> so how get all custom attributes of List<T> ?
}
You can create a simple extension method that will get the attributes you want from an implementation of the ICustomAttributeProvider interface (which is implemented by any representation of a .NET construct that can have an attribute on it):
public static IEnumerable<T> GetCustomAttributes(
this ICustomAttributeProvider provider, bool inherit) where T : Attribute
{
// Validate parameters.
if (provider == null) throw new ArgumentNullException("provider");
// Get custom attributes.
return provider.GetCustomAttributes(typeof(T), inherit).
Cast<T>();
}
From there, it's a call over all the PropertyInfo instances on a type, like so:
var attributes =
// Get all public properties, you might want to
// call a more specific overload based on your needs.
from p in obj.GetType().GetProperties()
// Get the attribute.
let attribute = p.GetCustomAttributes<GridColumnAttribute>().
// Assuming allow multiple is false.
SingleOrDefault().
// Filter out null properties.
where attribute != null
// Map property with attribute.
select new { Property = p, Attribute = attribute };
From there, you can call the GetType method on any object instance and run it through the above query to get the PropertyInfo instance and the attribute that is applied to it.
This is my POCO object:
public class ExampleTestOfDataTypes
{
public float FloatProp { get; set; }
public BoolWrapper BoolProp2 { get; set; }
}
This is configuration file of the POCO
public class ExampleTestOfDataTypesConfig : EntityTypeConfiguration<ExampleTestOfDataTypes>
{
public ExampleTestOfDataTypesConfig()
{
this.Property(x=>x.FloatProp).HasColumnName("CustomColumnName");
}
}
This is definition of EntityTypeConfiguration (the property configuration is just for example)
ExampleTestOfDataTypesConfig config = new ExampleTestOfDataTypesConfig();
I need to go through all the properties of class ExampleTestOfDataTypes, find all the properties which dataTypes are derived from Wrapper (BoolWrapper is) and then get these properties using lambda expression. Or anyhow select them by config.Property(...)
Type configPocoType = config.GetType().BaseType.GetGenericArguments()[0];
var poco = Activator.CreateInstance(configPocoType);
foreach (System.Reflection.PropertyInfo property in poco.GetType().GetProperties())
{
if (property.PropertyType.BaseType!=null&&
property.PropertyType.BaseType == typeof(Wrapper)
)
{
//TODO: Set property
//config.Property(x=>x.[What here]); //?
}
}
Thanks
Update
I didn't notice that the Property method is not your own implementation, sorry. It looks like you have to create an expression manually. This should work, or at least be close enough:
var parameter = Expression.Parameter(configPocoType);
var lambda = Expression.Lambda(
Expression.MakeMemberAccess(parameter, property),
parameter);
config.Property(lambda);
Original answer
It looks pretty likely that your existing Property method just uses an Expression to read the name of the property while maintaining compile-time safety. Most of the time such methods use reflection to pull out the property name into a string and then go on reflecting using the string name (possibly by calling another overload of Property that accepts a string).
Therefore, a reasonable approach is to invoke this other overload yourself, since your code already has a PropertyInfo in hand from which you can immediately get the property name.
If you only have one Property method, refactor by splitting it into two parts: one that pulls the name out of the Expression and one that works with the name; you can then call the second directly from your code.
I want to get value for a dynamic property of a dynamic object.
Here is my Code..
public string ReturnProperty(object ob, string prop)
{
Type type = ob.GetType();
PropertyInfo pr = type.GetProperty(prop);
//Here pr is null..Dont know whats wrong
return pr.GetValue(ob, null).ToString();
}
My guess is that either it isn't a public property, or you've got the name wrong, or it isn't a property at all (but a public field).
It's impossible to say more without knowing what the actual type is, but that should be a start.
You mention that this is a "dynamic object" but that's not really very descriptive. Bear in mind that the CLR itself doesn't know anything about the DLR - if you mean this is a type which implements IDynamicMetaObjectProvider or extends DynamicObject, then you won't be able to get at the properties with "normal" reflection like this.
In my case ob did not have pr getter setter properly.
//causes GetProperty to return null
public class MyClass{
public object pr;
}
//Works
public class MyClass{
public object pr { get; set; }
}
In my case, I had to define get and set. See post above
public string MyPropertyName { get; set; }
After this I could get the property by:
typeof(MyClassItem).GetProperty("PropertyName")
If the item you are attempting to access doesn't have getter and setter accessors, then most likely it is a field.
So your code would work as follows:
FieldInfo fieldInfo = type.GetField(fieldName);
Try the Type.GetProperty(String, BindingFlags) overload and select the right binding flags.
Example for ExpandoObject(it implements IDynamicMetaObjectProvider Jon Skeet mentioned):
public static string ReturnProperty(object ob, string prop)
{
if (ob is ExpandoObject)
{
return ((ExpandoObject)ob).Single(e => e.Key == prop).Value.ToString();
}
Type type = ob.GetType();
PropertyInfo pr = type.GetProperty(prop);
return pr.GetValue(ob, null).ToString();
}
//--
dynamic dyna = new ExpandoObject();
dyna.Name = "Element";
Console.WriteLine(ReturnProperty(dyna, "Name"));
I had the same error, the problem lies in the field names, if you have a field to read from the sql "isField" and its class has a field is named "IsField". The compiler will read case sensitive , for all reason it's a different field, for that reason you have null. Check yours casesensitive fields nomenculature.
I was trying to access a public property, but I was using BindingFlags.NonPublic instead of BindingFlags.Public.
I tried this & it worked.
public string ReturnProperty(object ob, string prop)
{
Type type = ob.GetType();
PropertyInfo pr = type.GetProperty(prop);
//Here pr is null..Dont know whats wrong
return pr.GetValue(ob, null).ToString();
}
ReturnProperty(new { abc = 10 }, "abc");
Whats wrong???
I just came across this issue when I was passing in the wrong data of a sorted grid view in an MVC project.
public HolidaysGridViewModel()
{
this.Sort = "HolidayDate"; // this was the wrong name
this.SortDir = "ASC";
}
It made me realize after reading your question that you were most likely passing in the name of a business from the database instead of the name of the database column object and therefore no were results were found which may have been the cause of your null value.
Is it possible to access a parent class from within an attribute.
For example I would like to create a DropDownListAttribute which can be applied to a property of a viewmodel class in MVC and then create a drop down list from an editor template. I am following a similar line as Kazi Manzur Rashid here.
He adds the collection of categories into viewdata and retrieves them using the key supplied to the attribute.
I would like to do something like the below,
public ExampleDropDownViewModel {
public IEnumerable<SelectListItem> Categories {get;set;}
[DropDownList("Categories")]
public int CategoryID { get;set; }
}
The attribute takes the name of the property containing the collection to bind to. I can't figure out how to access a property on the parent class of the attribute. Does anyone know how to do this?
Thanks
You can't access a parent type from an attribute. Attributes are metadata that is applied to a type, but you can't look back and try and identify the type, unless you did something like:
[MyCustomAttribute(typeof(MyClass))]
public class MyClass {
///
}
With the reflection solution above, you are not actually achieving the action of getting a type from an attribute, you are doing the reverse, you are getting attributes from a type. At this point, you already have the type.
You can do that using reflection. Do the following in your main class:
Type type = typeof(ExampleDropDownViewModel));
// Get properties of your data class
PropertyInfo[] propertyInfo = type.GetProperties( );
foreach( PropertyInfo prop in propertyInfo )
{
// Fetch custom attributes applied to a property
object[] attributes = prop.GetCustomAttributes(true);
foreach (Attribute attribute in attributes) {
// we are only interested in DropDownList Attributes..
if (attribute is DropDownListAttribute) {
DropDownListAttribute dropdownAttrib = (DropDownListAttribute)attribute;
Console.WriteLine("table set in attribute: " + dropdownAttrib.myTable);
}
}
}