I am using C# 4.5 and ASP.NET MVC 5.
I have the following:
[Required(ErrorMessage = "Prop1 is required")]
public string Prop1 { get;set;}
[Required(ErrorMessage = "Prop2 is required")]
public string Prop2 { get;set;}
As you see the error message is the property name plus the " is required" string. What I need is instead of typing the property name and the message for every property, use a generic method composer, that will return the name of the decorated property and a string that I add, something like:
public string GetMessage()
{
// Caller property should be a variable that is retrieved dynamically
// that holds the name of the property that called the function
return CallerProperty + " is required";
}
so now I can user:
[Required(ErrorMessage = GetMessage())]
public string Prop2 { get;set;}
so in brief: How can I know the property name that is decorated by an attribute.
Use reflection.
Pseudo
public List<Required> CallerProperty<T>(T source)
{
List<Required> result = new List<Required>();
Type targetInfo = target.GetType();
var propertiesToLoop = source.GetProperties();
foreach (PropertyInfo pi in propertiesToLoop)
{
Required possible = pi.GetCustomAttribute<Required>();
if(possible != null)
{
result.Add(possible);
string name = pi.Name; //This is the property name of the property that has a required attribute
}
}
return result;
}
This is just a demo of how to capture a custom attribute on a property. You have to work out how to manage a list of them, or whatever you need in order to generate the return type you need. Perhaps map it with the "pi.Name" also referenced? I don't know precisely what you need.
You can use the "nameof" expression as follows:
class Class1
{
[CustomAttr("prop Name: " + nameof(MyProperty))]
public int MyProperty { get; set; }
}
public class CustomAttr : Attribute
{
public CustomAttr(string test)
{
}
}
Related
I used to get one name for one property, but now I get data with two names for one property. That is, it was ServiceName ->ServiceName, and it became ServiceName ->ServiceName, Name.
value assignment code:
PropertyInfo property = item.GetType().GetProperty(column.Caption);
var range = worksheet.Cell(i, listIndex[indexForList]);
if (range == null)
{
continue;
}
var value = ChangeType((object)range.Value, property.PropertyType);
property.SetValue(item, value);
I have a model:
public class ImportAdditionalService
{
public string ServiceName { get; set; }
public decimal? Price { get; set; }
}
Are there any attributes in this case for class properties, so that I can use reflection for two names?
In my DB, all the tables have the status filed, but every table has a different name for that column. For e.g. the user table has user_status and the branch table has branch_status. All these columns of different tables would be having the same value. I have created POCO entities for all and wanted to create a generic function that would perform a query on the status field of the specified POCO entity class. So I wanted to create an attribute stating that this would be the status field of the specified entity class.
So, attribute that will give me the property name on which it is declared. The attribute would be declared only on a single property in the class. Till now I have done the following and it is working but wanted to know the efficient way to achieve the same. Below is my code:
Custom Attribute:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public class StatusFieldAttribute : Attribute
{
}
Declaration
public class UserTable
{
public int UserId{ get; set; }
[StatusField]
public int UserStatus { get; set; }
}
public class BranchTable
{
public int BranchId{ get; set; }
[StatusField]
public int BranchStatus { get; set; }
}
I want to get the property name from UserTable and BranchTable having the StatusField Attribute. I have achieve the same as:
Type type = typeof(UserTable);
string statusFieldName = type.GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(StatusFieldAttribute))).FirstOrDefault().Name;
And the above code gives the proper output as UserStatus. But is there an efficient way to achieve the same using something like below:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public class StatusFieldAttribute : Attribute
{
public StatusFieldAttribute([CallerMemberName] string propertyName = null)
{
StatusFieldName = propertyName;
}
public string StatusFieldName { get; }
}
And access the StatusFieldName to get the name of the property on which the StatusField attribute is declared in a particular class.
One approach is to define a marker interface (prior to C# 8), and define an extension method to retrieve the proper status field, like so:
interface IStatusFieldMarker
{
}
static class Extensions
{
public static string GetStatusFieldName(this IStatusFieldMarker t) =>
t.GetType()
.GetProperties()
.Single(p => Attribute.IsDefined(p, typeof(StatusFieldAttributeAttribute)))
.Name;
// optional getter/setter
public static int GetStatus(this IStatusFieldMarker t) =>
(int)(t.GetType()
.GetProperty(GetStatusFieldName(t))
.GetValue(t));
public static void SetStatus(this IStatusFieldMarker t, int value)
{
t.GetType()
.GetProperty(GetStatusFieldName(t))
.SetValue(t, value);
}
}
Then mark the POCO objects with the interface:
class Branch : IStatusFieldMarker
{
[StatusFieldAttribute]
public int BranchStatus { get; set; }
}
class User : IStatusFieldMarker
{
[StatusFieldAttribute]
public int UserStatus { get; set; }
}
You can then call the GetStatusFieldName extension method to get the field name:
Branch b = new Branch() { BranchStatus = 3 };
User u = new User() { UserStatus = 1 };
Console.WriteLine(b.GetStatusFieldName()); // prints BranchStatus
Console.WriteLine(u.GetStatusFieldName()); // prints UserStatus
Console.WriteLine(b.GetStatus()); // prints 3
Console.WriteLine(u.GetStatus()); // prints 1
I simply created below function to get the status name
public static string GetStatusField<T>(this T type) where T : Type
{
PropertyInfo propertyInfo = type.GetProperties().FirstOrDefault(prop => Attribute.IsDefined(prop, typeof(StatusFieldAttribute)));
if (propertyInfo == null)
{
Log.Error($"StatusFieldAttribute not found in {type.Name} ");
throw new Exception($"StatusFieldAttribute not found in { type.Name } ");
}
else
return propertyInfo.Name;
}
And invoked the function as
Type type = typeof(UserTable);
string statusFieldName = type.GetStatusField();
The specific functionality of MVC model validation that I want to leverage is validating data BEFORE it has been assigned to properties of an object instance.
For example if I have the class:
public class Test
{
[Required(ErrorMessage="Id is required")]
public int Id { get; set; }
[Required(ErrorMessage="Name is required")]
[RegularExpression(Constants.SomeRegex, ErrorMessage = "Please enter a valid value for Name")]
public int Name { get; set; }
}
I want to be able to validate that the value assigned to 'Id' can at least be assigned before trying to create an instance. In this case that would mean being assignable to an integer - so the value "ABC" would fail validation.
Of course I can't create an instance of Test with the value "ABC" for Id, it's not assignable to Int32.
MVC controllers implement this functionality - errors will be reported back before an instance of the model class can be created.
To this end, I have so far attempted using System.CompondentModel.DataAnnotations.Validator
public bool IsValid(IDictionary<object, object> data, out OfferConfig offerConfig)
{
offerConfig = new OfferConfig();
var context = new ValidationContext(offerConfig, data);
var results = new List<ValidationResult>();
return Validator.TryValidateObject(offerConfig, context, results, true);
}
And passing in an instance implementing IDictionary
var dict = new Dictionary<object, object>
{
{"Id", dataTable.Rows[i][0].ToString()},
{"Name", dataTable.Rows[i][1].ToString()}
}
Like so:
Test testInstance;
bool isValid = IsValid(dict, out testInstance);
But maybe the Validator doesn't work as I'm expecting. Is the data argument supposed to be string object representations of the model properties? The validation results out appear as if values simply have not been assigned rather than being incorrect.
Hopefully someone can see what I'm trying to achieve here...
Simply create new validation attribute in which you will place our validation logic. Like this:
public class StringIdLengthRangeAttribute : ValidationAttribute
{
public int Minimum { get; set; }
public int Maximum { get; set; }
public StringLengthRangeAttribute()
{
this.Minimum = 0;
this.Maximum = int.MaxValue;
}
public override bool IsValid(object value)
{
string strValue = value as string;
if (!string.IsNullOrEmpty(strValue))
{
int len = strValue.Length;
return len >= this.Minimum && len <= this.Maximum;
}
return true;
}
}
This is example validation of length - replace it with your validation logic.
And your class:
public class Test
{
[Required]
[StringIdLengthRange(Minimum = 10, Maximum = 20)]
public string Id { get; set; }
}
With such attribute you are able to use that logic on any other fields.
My code below:
foreach (var PI in ObjType.GetProperties())
{
var metaData = ModelMetadataProviders.Current.GetMetadataForType(null, PI.GetType());
string DispName = metaData.DisplayName
}
ObjType is the type of an EF6 schema first entity with DisplayName been added as a Metadata class. The error above is probably because PI.GetType() returns the type of the PropertyInfo. But I really can't figure out how to get the property itself.
I have look into various example using:
ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
However, in my case, I am not using any Lambda Expression. I just need to construct a list of the properties' DisplayName and pass it on.
But I really can't figure out how to get the property itself.
You want PropertyInfo.PropertyType, so change PI.GetType() to PI.PropertyType.
I don't know if this will help you, but this is how i got the MetaDataClassType from the object it has been attached to.
Example Class with a MetadataType:
[MetadataType(typeof(TheMetaDataYouWantTheTypeFrom))]
public class ObjectYouWantMetaDataTypeFrom
{
public string Username { get; set; }
public string Name { get; set; }
}
public class TheMetaDataYouWantTheTypeFrom
{
[Required(ErrorMessage = "You must enter a username.")]
public object Username { get; set; }
[Required(ErrorMessage = "You must enter a name.")]
public object Name { get; set; }
}
The Code to get the MetadataClassType
Type ObjectType = ObjectYouWantMetaDataTypeFrom.GetType();
object ObjectMetaData = ObjectType.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault();
MetadataTypeAttribute MetaData = ObjectMetaData as MetadataTypeAttribute;
if (MetaData == null)
{ throw new NullReferenceException(); }
Type metadataClassType = MetaData.MetadataClassType;
In the list of objects of type
List<Configuracion.Models.v_cCfgDeclaraciones>
I would like retrive the name field, I would like to find the name of the field within the loop (foreach) better if I do not use reflection, how i can resolve this?
Put together something on Linqpad. Note that this does use reflection:
void Main() {
Test t = new Test();
Type tType = t.GetType();
foreach (PropertyInfo prop in tType.GetProperties()) {
prop.Name.Dump();
}
}
public class Test {
public string prop1 { get; set; }
public string prop2 { get; set; }
public Test() {
prop1 = "Property 1";
prop2 = "Property 2";
}
}
I don't know of a way you can get this information (short of hard-coding it in) without using reflection. Is there a reason you don't want to use reflection in this case?