I have a custom attribute, inside the constructor of my custom attribute I want to set the value of a property of my attribute to the type of the property my attribute was applied to, is there someway to access the member that the attribute was applied to from inside my attribute class?
It's possible from .NET 4.5 using CallerMemberName:
[SomethingCustom]
public string MyProperty { get; set; }
Then your attribute:
[AttributeUsage(AttributeTargets.Property)]
public class SomethingCustomAttribute : Attribute
{
public StartupArgumentAttribute([CallerMemberName] string propName = null)
{
// propName == "MyProperty"
}
}
Attributes don't work that way, I'm afraid. They are merely "markers", attached to objects, but unable to interact with them.
Attributes themselves should usually be devoid of behaviour, simply containing meta-data for the type they are attached to. Any behaviour associated with an attribute should be provided by another class which looks for the presence of the attribute and performs a task.
If you are interested in the type the attribute is applied to, that information will be available at the same time you are reflecting to obtain the attribute.
You can do next. It is simple example.
//target class
public class SomeClass{
[CustomRequired(ErrorMessage = "{0} is required", ProperytName = "DisplayName")]
public string Link { get; set; }
public string DisplayName { get; set; }
}
//custom attribute
public class CustomRequiredAttribute : RequiredAttribute, IClientValidatable
{
public string ProperytName { get; set; }
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var propertyValue = "Value";
var parentMetaData = ModelMetadataProviders.Current
.GetMetadataForProperties(context.Controller.ViewData.Model, context.Controller.ViewData.Model.GetType());
var property = parentMetaData.FirstOrDefault(p => p.PropertyName == ProperytName);
if (property != null)
propertyValue = property.Model.ToString();
yield return new ModelClientValidationRule
{
ErrorMessage = string.Format(ErrorMessage, propertyValue),
ValidationType = "required"
};
}
}
Related
I have a custom attribute, inside the constructor of my custom attribute I want to set the value of a property of my attribute to the type of the property my attribute was applied to, is there someway to access the member that the attribute was applied to from inside my attribute class?
It's possible from .NET 4.5 using CallerMemberName:
[SomethingCustom]
public string MyProperty { get; set; }
Then your attribute:
[AttributeUsage(AttributeTargets.Property)]
public class SomethingCustomAttribute : Attribute
{
public StartupArgumentAttribute([CallerMemberName] string propName = null)
{
// propName == "MyProperty"
}
}
Attributes don't work that way, I'm afraid. They are merely "markers", attached to objects, but unable to interact with them.
Attributes themselves should usually be devoid of behaviour, simply containing meta-data for the type they are attached to. Any behaviour associated with an attribute should be provided by another class which looks for the presence of the attribute and performs a task.
If you are interested in the type the attribute is applied to, that information will be available at the same time you are reflecting to obtain the attribute.
You can do next. It is simple example.
//target class
public class SomeClass{
[CustomRequired(ErrorMessage = "{0} is required", ProperytName = "DisplayName")]
public string Link { get; set; }
public string DisplayName { get; set; }
}
//custom attribute
public class CustomRequiredAttribute : RequiredAttribute, IClientValidatable
{
public string ProperytName { get; set; }
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var propertyValue = "Value";
var parentMetaData = ModelMetadataProviders.Current
.GetMetadataForProperties(context.Controller.ViewData.Model, context.Controller.ViewData.Model.GetType());
var property = parentMetaData.FirstOrDefault(p => p.PropertyName == ProperytName);
if (property != null)
propertyValue = property.Model.ToString();
yield return new ModelClientValidationRule
{
ErrorMessage = string.Format(ErrorMessage, propertyValue),
ValidationType = "required"
};
}
}
Is it possible to access the type of a property from an attribute that has been implemented on to that property?
public class FooAttribute : Attribute
{
public string GetPropertyName()
{
// return ??
}
}
public class Bar
{
[FooAttribute]
public int Baz { get; set; }
}
I would like GetPropertyName() to return "Baz".
Sriram Sakthivel is correct that this is not possible but if you are using .net 4.5 you can create a workaround using the CallerMemberNameAttribute to pass the caller into the constructor of your attribute, store it and then return it from your GetPropertyName method:
public class FooAttribute : Attribute
{
public string PropertyName { get; set; }
public FooAttribute([CallerMemberName] string propertyName = null)
{
PropertyName = propertyName;
}
public string GetPropertyName()
{
return PropertyName;
}
}
This will pass the caller (the property) to the constructor of your attribute.
More details on the CallerMemberNameAttribute are available on MSDN.
What you're asking is not possible. Because Attributes and properties doesn't have "One to One" relationship. You can apply FooAttribute to any number of Properties, In such case which property you need to return from GetPropertyName method?
As I said in comments you can loop through all the types and its properties to see which are all the properties have FooAttribute but obviously that's not what you want.
I have a custom attribute like this:
public class PropertyInfoAttribute : Attribute
{
public bool IsAutoComplete { get; set; }
}
And there is a class like this:
public class Article
{
public virtual int Order { get; set; }
//other properties
}
In another class,which inherits from Article, I override Order property and declare the attribute for it like this:
public class ArticleDetails : Article
{
[PropertyInfo(IsAutoCompele = true)]
public override int Order { get; set; }
}
The problem appears when I want to get attributes by using the GetCustomAttributes method in PropertyInfo class. I do it like this:
PropertyInfo propInfo = //do something for getting property info from the
//ArticleDetails class;
var attr = propInfo.GetCustomAttribute<PropertyInfoAttribute>();
But it returns nothing! I don't know why!
UPDATE:
I get property info in this method:
public static void InitPropertyInfoAttribute<TModel, TProperty>(MvcHtmlString source, Expression<Func<TModel, TProperty>> expression)
{
PropertyInfo propInfo = (expression.Body as MemberExpression).Member as PropertyInfo;
}
I think the problem hides in here:
PropertyInfo propInfo = //do something for getting property info from the
//ArticleDetails class;
I presume that you actually obtain this property info from Article class, not ArticleDetails and that's why it returns null. The following snippet worked as expected for me:
PropertyInfo propInfo = typeof(ArticleDetails).GetProperty("Order");
var attr = propInfo.GetCustomAttribute<PropertyInfoAttribute>();
Update
According to your update - the problem is that Member property of the MemberExpression points to the Article type;
As a solution to this you can update your InitPropertyInforAttribute as follows:
MemberExpression memberExpression = (expression.Body as MemberExpression);
return typeof(TModel).GetProperty(memberExpression.Member.Name);
And don't forget that you should pass ArticleDetails as first generic type parameter - InitPropertyInfoAttribute<ArticleDetails, propertyType>.
Sorry, but I can't reproduce the error. Attribute is extracted. Could you provide the details?
// Your classes
public class PropertyInfoAttribute: Attribute {
public bool IsAutoComplete {
get;
set;
}
}
public class Article {
public virtual int Order {
get;
set;
}
}
public class ArticleDetails: Article {
[PropertyInfo(IsAutoComplete = true)]
public override int Order {
get;
set;
}
}
...
// My test
// Let's do it explicitly:
// ask for public and instance (not static) property
PropertyInfo pi = typeof(ArticleDetails).GetProperty("Order", BindingFlags.Public | BindingFlags.Instance);
// Then ask for the attribute
Attribute at = pi.GetCustomAttribute(typeof(PropertyInfoAttribute));
// And, finally, check if attribute is existing
// ... And so, assertion passes - attribute is existing
Trace.Assert(!Object.ReferenceEquals(null, at), "No Attribute found.");
I have a class (Application) that has multiple properties of the type of another custom class (Employment). I would like to validate that Employment class conditionally based on whether the property of the Application class is marked with [Required].
From what I've found, I think I should be utilizing the IValidatableObject interface for Employment. The problem is that I'm not sure how to use reflection (or something else maybe) to check if this instance of the class is annotated with the [Required] attribute to determine whether to validate it or not.
Maybe this isn't even possible. I initially set up two classes for the Employment class: Employment and EmploymentRequired. Only the latter had the validation attributes on its properties. It works, but I'd like to just have one class to use if possible.
public class Application
{
[Required]
public Employment Employer1 { get; set; }
public Employment Employer2 { get; set; }
}
public class Employment : IValidatableObject
{
[Required]
public string EmployerName { get; set; }
[Required]
public string JobTitle { get; set; }
public string Phone { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
var t = this.GetType();
//var pi = t.GetProperty("Id");
//var isRequired = Attribute.IsDefined(pi, typeof(RequiredAttribute));
//how can I get the attributes of this property in Application class?
if (isRequired)
{
Validator.TryValidateProperty(this.EmployerName,
new ValidationContext(this, null, null) { MemberName = "EmployerName" }, results);
Validator.TryValidateProperty(this.JobTitle,
new ValidationContext(this, null, null) { MemberName = "JobTitle" }, results);
}
return results;
}
}
You should be able to check for the required attribute using Attribute.IsDefined.
http://msdn.microsoft.com/en-us/library/system.attribute.isdefined.aspx
Seems like you can't do this, because using reflection you can't get parent object/class that references your current instance and all the more so reference property information.
EDIT: Maybe you can make Employment type Generic with required and non required validation modes?
I think you are searching for the Attribute.IsDefined method. You would have to first obtain the reference to the field itself, and then validate the presence of the attribute. Like the following (adapted from the example at MSDN):
// Get the class type (you can also get it directly from an instance)
Type clsType = typeof(Application);
// Get the FieldInfo object
FieldInfo fInfo = clsType.GetField("Employer1");
// See if the Required attribute is defined for the field
bool isRequired = Attribute.IsDefined(fInfo , typeof(RequiredAttribute));
Since what I'm trying to do doesn't seem to be possible exactly, I found a different way to do it, based on the suggestion of #user1578874. I added an IsRequired property to Employment and used MVC Foolproof Validation to mark those properties as [RequiredIf("IsRequired")]. Seems to be the cleanest solution.
I am working an ASP.net MVC4 website and have model & view model layer. Because of certain reasons I have different names for few properties in Model and ViewModel
Model
public partial class Project
{
public string Desc {get; set;}
}
View Model
public class ProjectViewModel
{
public string Description { get; set; }
}
Now at model layer, I need to use ViewModel name of a property if it is different. I was thinking of creating a custom attribute so that I can have something like this in models:
public partial class Project
{
[ViewModelPropertyName("Description")]
public string Desc {get;set;}
}
and use it at model layer as
string.Format("ViewModel Property Name is {0}", this.Desc.ViewModelPropertyName())
I want this to generic so that if there is no ViewModelPropertyName attribute on a property then it should return the same property name i.e. if Desc property has no attribute then it should return "Desc" only.
Here is what I tried
public class ViewModelPropertyNameAttribute : System.Attribute
{
#region Fields
string viewModelPropertyName;
#endregion
#region Properties
public string GetViewModelPropertyName()
{
return viewModelPropertyName;
}
#endregion
#region Constructor
public ViewModelPropertyNameAttribute(string propertyName)
{
this.viewModelPropertyName = propertyName;
}
#endregion
}
Need help for how to access custom attribute
Current state
public static class ModelExtensionMethods
{
public static string ViewModelPropertyName(this Object obj)
{
// ERROR: Cannot convert from 'object' to 'System.Reflect.Assembly'
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(obj);
foreach (System.Attribute attr in attrs)
{
if (attr is ViewModelPropertyNameAttribute)
{
return ((ViewModelPropertyNameAttribute)attr).GetViewModelPropertyName();
}
}
return string.Empty;
}
}
But this has compile time error:
Unfortunately you can not get the attributes you used to decorate the properties by reflecting on the type of the property itself. I have therefore modified your ViewModelPropertyName(this object) Extension method slightly to take in the name of your desired property.
This method will now take in the name of the property whose attribute you wish to get. If the attribute exists it will return the value passed to its constructor, if it on the other hand, does not exist it will simply return the name of the property you passed in.
public static class ModelExtensionMethods
{
public static string ViewModelPropertyName(this object obj, string name)
{
var attributes = obj.GetType()
.GetCustomAttributes(true)
.OfType<MetadataTypeAttribute>()
.First()
.MetadataClassType
.GetProperty(name)
.GetCustomAttributes(true);
if (attributes.OfType<ViewModelPropertyNameAttribute>().Any())
{
return attributes.OfType<ViewModelPropertyNameAttribute>()
.First()
.GetViewModelPropertyName();
}
else
{
return name;
}
}
}
You can also define the following classes to test this new approach.
[MetadataType(typeof(TestClassMeta))]
class TestClass { }
class TestClassMeta
{
[ViewModelPropertyName("TheName")]
public string FirstName { get; set; }
public string LastName { get; set; }
}
Also, as you can see from the following lines of code, your ViewModelPropertyName(this object, string) Extension method will now be called on the instance of your TestClass, instead of calling it on the property itself.
class Program
{
static void Main()
{
Console.WriteLine(new TestClass().ViewModelPropertyName("FirstName"));
Console.WriteLine(new TestClass().ViewModelPropertyName("LastName"));
Console.Read();
}
}