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.
Related
On a control I am using multiple attribute properties:
[Browsable(false)]
[Bindable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Obsolete("", true)]
public new Boolean AllowDrop;
I am using those properties on a lot of the other control properties as well.
I am wondering if there is a way to reduce the amount of code to write each time.
It would be nice if I could combine multiple attributes like this:
[Hidden(true)]
public new Boolean AllowDrop;
Where the Hidden Property would include all the attributes above. So there is only 1 single line of code.
Maybe there is also a way to combine the attributes in a macro or something?
I am aware that there are other ways of hiding properties but I chose the way of using attributes.
Thanks
It depends to the framework which is using the attribute.
Combining attributes can be meaningful in order to the context which uses and interprets attributes. For example for those contexts which use .Net Type Description mechanisms you can customize the type description which .Net returns to consumers.
It's possible to provide custom metadata for types using the standard .Net mechanism for that purpose, registering a custom type descriptor for your object.
The idea will work this way, you create a custom type descriptor for your type. In the custom type descriptor, you return custom property descriptors for the properties of your type and in the property descriptor, you return a custom set of attributes for the property.
The approach requires more code, but it's really interesting and shares some good idea about how to provide custom metadata for your types:
IMetedataAttribute Interface
The usage is providing an standard way to create MetaDataAttributes. Each attribute which implements this interface will be used as metadata and instead of the attribute, those one which it returns in Process method will be used:
public interface IMetadatAttribute
{
Attribute[] Process();
}
Sample MetadataAttribute
It's a sample metadata attribute which returns some attribute instead when processing the attribute:
public class MySampleMetadataAttribute : Attribute, IMetadatAttribute
{
public Attribute[] Process()
{
var attributes = new Attribute[]{
new BrowsableAttribute(false),
new EditorBrowsableAttribute(EditorBrowsableState.Never),
new BindableAttribute(false),
new DesignerSerializationVisibilityAttribute(
DesignerSerializationVisibility.Hidden),
new ObsoleteAttribute("", true)
};
return attributes;
}
}
Property Descriptor
This class will be used by the custom type descriptor to provide a custom list of attributes for the property:
public class MyPropertyDescriptor : PropertyDescriptor
{
PropertyDescriptor original;
public MyPropertyDescriptor(PropertyDescriptor originalProperty)
: base(originalProperty) { original = originalProperty;}
public override AttributeCollection Attributes
{
get
{
var attributes = base.Attributes.Cast<Attribute>();
var result = new List<Attribute>();
foreach (var item in attributes)
{
if(item is IMetadatAttribute)
{
var attrs = ((IMetadatAttribute)item).Process();
if(attrs !=null )
{
foreach (var a in attrs)
result.Add(a);
}
}
else
result.Add(item);
}
return new AttributeCollection(result.ToArray());
}
}
// Implement other properties and methods simply using return original
// The implementation is trivial like this one:
// public override Type ComponentType
// {
// get { return original.ComponentType; }
// }
}
Type Descriptor
This is the type descriptor which provides a custom description for your type. In this example it uses custom property descriptors to provide custom attributes set for the properties of your class:
public class MyTypeDescriptor : CustomTypeDescriptor
{
ICustomTypeDescriptor original;
public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
: base(originalDescriptor)
{
original = originalDescriptor;
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Select(p => new MyPropertyDescriptor(p))
.ToArray();
return new PropertyDescriptorCollection(properties);
}
}
Typedescriptor Provider
This class will be used in the attribute above your type to introduce the custom type descriptor which we created as the metadata engine for the type:
public class MyTypeDescriptionProvider : TypeDescriptionProvider
{
public MyTypeDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(object))) { }
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance)
{
ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(objectType, instance);
return new MyTypeDescriptor(baseDescriptor);
}
}
Sample Class
Here is my sample class which its Name property is decorated using MySampleMetadataAttribute and the class itself is registered to use our custom type descriptor provider:
[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class MySampleClass
{
public int Id { get; set; }
[MySampleMetadataAttribue]
[DisplayName("My Name")]
public string Name { get; set; }
}
To see the result it's enough to create an instance of the class and see the result in PropertyGrid:
var o = new MySampleClass();
this.propertyGrid1.SelectedObject = o;
Some notes on answer
Probably it's not as simple as you expected for such task. But it's working.
It's a lengthy answer, but contains a complete working example of how you can apply type descriptors to your types to provide custom metadata.
The approach will not work for engines which use reflection instead of type description. But it's completely working with for example PropertyGrid control which works with type description.
The best way for me to do this, is by using Metalama (modern rewrite of PostSharp for new .NET releases).
It is absolutely the best framework for doing AOP in .NET from the same guys that did PostSharp. It is still in preview, but Metalama 1.0 will be released in a week or 2, and in next year, it will probably get most of features found in PostSharp... And it has a nice community on Slack and the authors of this Metalama framework are super supportive, they helped me with each question I had, and I had a lot of them already :D
And so this library is perfect for creating custom aspects, but could easily be used for this merging of attributes :) It will be even better then the approach above, because once you see transformed file (using Metalama Diff Preview - you gotta install Metalama extension to VS), then you will actually see all those original attributes there, in a transformed file :)
And so this is how easily I will merge 3 attributes into 1 with Metalama:
Metalama easy merging of attributes 1
(This would be for aspect attributes created by Metalama)
Or, for other attributes (from other libraries), that don't need to do the aspect work, it would by like this: (And this is probably what you want to use, not the first example...):
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 have a series of static methods to modify a collection then return the modified collection:
private static IEnumerable<Invoice> ResolveProxies(IEnumerable<Invoice> e) {
// do something to e
return e;
}
private static IEnumerable<Person> ResolveProxies(IEnumerable<Person> e) {
// do something to e
return e;
}
In another part of the application there is a method to decide if a collection is of a certain type, so that it can be converted to that type and have its corresponding ResolveProxies method called:
public static GridModel<T> ToGridModel<T>(this GridModel gridModel) {
// gridModel.Data is just IEnumerable
var collection = gridModel.Data as IEnumerable<T> ?? new List<T>();
return new GridModel<T> {
Data = EvaluateDynamicProxies(collection),
Total = gridModel.Total
};
}
private static IEnumerable<T> EvaluateProxies<T>(IEnumerable<T> collection) {
if (collection is IEnumerable<Invoice>) {
var enumeration = (collection as IEnumerable<Invoice>);
return ResolveProxies(enumeration) as IEnumerable<T>;
}
if (collection is IEnumerable<Person>) {
var enumeration = (collection as IEnumerable<Person>);
return ResolveProxies(enumeration) as IEnumerable<T>;
}
// proxy resolution isn't needed so return the unchanged collection
return collection;
}
Having such repetitive conditional logic is bad code smell. I'm struggling to come up with some way to mark particular types so that I know they have a corresponding proxy resolver method. Something like this perhaps:
public interface IProxyResolver<out T> where T:IEnumerable<T> {
T ResolveProxies();
}
But how would I use this? In effect I need a way to ask the compiler:
Does T have a matching ResolveProxies method?
What is the name of the class or method that resolves proxies for T so that I can get an instance of it and call it?
You could use an inversion of control (IOC) framework. For example, my team uses Castle Windsor. You can register services (usually interfaces) and types that provide the services. It has some nice generics resolution, so you can do things like this:
interface IProxyResolver<T> { /* whatever */ }
class ProxyResolver<T> : IProxyResolver<T> { /* ... */ }
class PersonProxyResolver : ProxyResolver<Person> { }
class InvoiceProxyResolver : ProxyResolver<Invoice> { }
then, you can summon these types like this:
void SomeMethodThatNeedsAProxyResolver<T>(T obj)
{
var resolver = ioc.Resolve<IProxyResolver<T>>();
//...
}
If you've regsitered the classes above, when T is Person or Invoice, you get the correct non-generic subclass of ProxyResolver; if it is any other type, you get the default generic superclass. Of course, you can structure things differently; if you need a specific proxy resolver for every type, that's possible too.
How about using a custom attribute? This is how custom serializers are selected, etc.
You'd start by defining the Attribute class:
public class ProxyResolverAttribute : Attribute {
public Type ResolverType { get; set; }
public ProxyResolver(Type resolverType) { ResolverType = resolverType; }
}
and then put that on the type contained, e.g.
[ProxyResolver(TypeOf(InvoiceProxyResolver))]
public class Invoice ... { ... }
then use reflection to see if the generic type used in the collection specifies a proxy resolver type:
// Untested, beware of bugs
var enumerationGenericType = enumeration.GetType().GetGenericArguments().FirstOrDefault();
var resolverAttribute = enumerationGenericType.GetType().GetCustomAttributes(TypeOf(ProxyResolverAttribute)).FirstOrDefault();
if (resolverAttribute != null) {
var resolverType = resolverAttribute.ResolverType;
// instanciate something of resolverType here
}
EDIT: Reading the comments, if you don't want to apply the attributes to the contained objects, I'd suggest creating custom classes which inherit List and apply the attribute there, e.g.
[ProxyResolver(TypeOf(InvoiceProxyResolver))]
public class InvoiceList : List<Invoice>
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);
}
}
}
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.