I want to write an extension method to get the value of the MaximumLength property on the StringLength attribute.
For example, I have a class:
public class Person
{
[StringLength(MaximumLength=1000)]
public string Name { get; set; }
}
I want to be able to do this:
Person person = new Person();
int maxLength = person.Name.GetMaxLength();
Would this be possible using some sort of reflection?
If you use LINQ expressions, you can pull out the information via reflection with slightly different syntax (and you get to avoid defining an extension method on a commonly used string type):
public class StringLength : Attribute
{
public int MaximumLength;
public static int Get<TProperty>(Expression<Func<TProperty>> propertyLambda)
{
MemberExpression member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a method, not a property.",
propertyLambda.ToString()));
PropertyInfo propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format(
"Expression '{0}' refers to a field, not a property.",
propertyLambda.ToString()));
var stringLengthAttributes = propInfo.GetCustomAttributes(typeof(StringLength), true);
if (stringLengthAttributes.Length > 0)
return ((StringLength)stringLengthAttributes[0]).MaximumLength;
return -1;
}
}
So your Person class might be:
public class Person
{
[StringLength(MaximumLength=1000)]
public string Name { get; set; }
public string OtherName { get; set; }
}
Your usage might look like:
Person person = new Person();
int maxLength = StringLength.Get(() => person.Name);
Console.WriteLine(maxLength); //1000
maxLength = StringLength.Get(() => person.OtherName);
Console.WriteLine(maxLength); //-1
You can return something other than -1 for a property that didn't have that attribute defined. You weren't specific, but that's easy to change.
This may not be the nicest way to do this, but if you dont mind suppling the property name you need to get the Attribute value for you could use something like
public static class StringExtensions
{
public static int GetMaxLength<T>(this T obj, string propertyName) where T : class
{
if (obj != null)
{
var attrib = (StringLengthAttribute)obj.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance)
.GetCustomAttribute(typeof(StringLengthAttribute), false);
if (attrib != null)
{
return attrib.MaximumLength;
}
}
return -1;
}
}
Usage:
Person person = new Person();
int maxLength = person.GetMaxLength("Name");
Otherwise using a function like Chris Sinclair mentioned in his comment would work nicely
Related
I have the following custom attribute, which can be applied on properties:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class IdentifierAttribute : Attribute
{
}
For example:
public class MyClass
{
[Identifier()]
public string Name { get; set; }
public int SomeNumber { get; set; }
public string SomeOtherProperty { get; set; }
}
There will also be other classes, to which the Identifier attribute could be added to properties of different type:
public class MyOtherClass
{
public string Name { get; set; }
[Identifier()]
public int SomeNumber { get; set; }
public string SomeOtherProperty { get; set; }
}
I then need to be able to get this information in my consuming class.
For example:
public class TestClass<T>
{
public void GetIDForPassedInObject(T obj)
{
var type = obj.GetType();
//type.GetCustomAttributes(true)???
}
}
What's the best way of going about this?
I need to get the type of the [Identifier()] field (int, string, etc...) and the actual value, obviously based on the type.
Something like the following,, this will use only the first property it comes accross that has the attribute, of course you could place it on more than one..
public object GetIDForPassedInObject(T obj)
{
var prop = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(p => p.GetCustomAttributes(typeof(IdentifierAttribute), false).Count() ==1);
object ret = prop !=null ? prop.GetValue(obj, null) : null;
return ret;
}
public class TestClass<T>
{
public void GetIDForPassedInObject(T obj)
{
PropertyInfo[] properties =
obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
PropertyInfo IdProperty = (from PropertyInfo property in properties
where property.GetCustomAttributes(typeof(Identifier), true).Length > 0
select property).First();
if(null == IdProperty)
throw new ArgumentException("obj does not have Identifier.");
Object propValue = IdProperty.GetValue(entity, null)
}
}
A bit late but here is something I did for enums (could be any object also) and getting the description attribute value using an extension (this could be a generic for any attribute):
public enum TransactionTypeEnum
{
[Description("Text here!")]
DROP = 1,
[Description("More text here!")]
PICKUP = 2,
...
}
Getting the value:
var code = TransactionTypeEnum.DROP.ToCode();
Extension supporting all my enums:
public static string ToCode(this TransactionTypeEnum val)
{
return GetCode(val);
}
public static string ToCode(this DockStatusEnum val)
{
return GetCode(val);
}
public static string ToCode(this TrailerStatusEnum val)
{
return GetCode(val);
}
public static string ToCode(this DockTrailerStatusEnum val)
{
return GetCode(val);
}
public static string ToCode(this EncodingType val)
{
return GetCode(val);
}
private static string GetCode(object val)
{
var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : string.Empty;
}
Here is a more real-word example. We use an extension method and check if a property contains a FieldMetaDataAttribute (a custom attribute in my source code base)
with valid Major and MinorVersion. What is of general interest is the part where we use the parent class type and GetProperties and retrieve the ProperyInfo and then use GetCustomAttribute to retrieve a attribute FieldMetaDataAttribute in this special case. Use this code for inspiration how to do more generic way of retrieving a custom attribute. This can of course be polished to make a general method to retrieve a given attribute of any property of a class instance.
/// <summary>
/// Executes the action if not the field is deprecated
/// </summary>
/// <typeparam name="TProperty"></typeparam>
/// <typeparam name="TForm"></typeparam>
/// <param name="form"></param>
/// <param name="memberExpression"></param>
/// <param name="actionToPerform"></param>
/// <returns>True if the action was performed</returns>
public static bool ExecuteActionIfNotDeprecated<TForm, TProperty>(this TForm form, Expression<Func<TForm, TProperty>> memberExpression, Action actionToPerform)
{
var memberExpressionConverted = memberExpression.Body as MemberExpression;
if (memberExpressionConverted == null)
return false;
string memberName = memberExpressionConverted.Member.Name;
PropertyInfo matchingProperty = typeof(TForm).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.FirstOrDefault(p => p.Name == memberName);
if (matchingProperty == null)
return false; //should not occur
var fieldMeta = matchingProperty.GetCustomAttribute(typeof(FieldMetadataAttribute), true) as FieldMetadataAttribute;
if (fieldMeta == null)
{
actionToPerform();
return true;
}
var formConverted = form as FormDataContract;
if (formConverted == null)
return false;
if (fieldMeta.DeprecatedFromMajorVersion > 0 && formConverted.MajorVersion > fieldMeta.DeprecatedFromMajorVersion)
{
//major version of formConverted is deprecated for this field - do not execute action
return false;
}
if (fieldMeta.DeprecatedFromMinorVersion > 0 && fieldMeta.DeprecatedFromMajorVersion > 0
&& formConverted.MinorVersion >= fieldMeta.DeprecatedFromMinorVersion
&& formConverted.MajorVersion >= fieldMeta.DeprecatedFromMajorVersion)
return false; //the field is expired - do not invoke action
actionToPerform();
return true;
}
So, I thaught I have a solution for getting a PropertyInfo when having a concrete class, and a PropertyInfo for an interface implemented by the concrete class. Here is the code:
public static PropertyInfo GetImplementingProperty(Type concreteType, PropertyInfo interfaceProperty)
{
// do some region parameter check, skipped
var interfaceType = interfaceProperty.DeclaringType;
//use the set method if we have a write only property
var getCorrectMethod = interfaceProperty.GetGetMethod() == null
? (Func<PropertyInfo, MethodInfo>) (p => p.GetSetMethod(true))
: p => p.GetGetMethod(true);
var propertyMethod = getCorrectMethod(interfaceProperty);
var mapping = concreteType.GetInterfaceMap(interfaceType);
MethodInfo targetMethod = null;
for (var i = 0; i < mapping.InterfaceMethods.Length; i++)
{
if (mapping.InterfaceMethods[i] == propertyMethod)
{
targetMethod = mapping.TargetMethods[i];
break;
}
}
foreach (var property in concreteType.GetProperties(
BindingFlags.Instance | BindingFlags.GetProperty |
BindingFlags.Public | BindingFlags.NonPublic)) // include non-public!
{
if (targetMethod == getCorrectMethod(property)) // include non-public!
{
return property;
}
}
throw new InvalidOperationException("The property {0} defined on the interface {1} has not been found on the class {2}. That should never happen."
.FormatText(interfaceProperty.Name, interfaceProperty.DeclaringType.FullName, concreteType.FullName));
}
Unfortunately I found a case where it fails, and I am unsure how to fix this.
So I have in a dll a class:
public abstract class BaseClass
{
public Guid ConfigId { get; set; }
public virtual Guid ConfigId2 { get; set; }
}
Then in another dll I do:
interface INamed
{
Guid ConfigId { get; }
Guid ConfigId2 { get; }
}
private class SuperClass : BaseClass, INamed
{
}
Now
ReflectionHelper.GetImplementingProperty(typeof(SuperClass), typeof(INamed).GetProperty("ConfigId2")); // this works
ReflectionHelper.GetImplementingProperty(typeof(SuperClass), typeof(INamed).GetProperty("ConfigId")); // this fails
Any idea how do I match up the ConfigId property to the Base class proprety definition?
PS. I have attributes on the concrete class properties, thats why i need to get those.
Any help appreciated!
You need to add BindingFlags.FlattenHierarchy to your GetProperties call in order to get parent class properties. See the documentation at https://msdn.microsoft.com/en-us/library/kyaxdd3x(v=vs.110).aspx
I have a class that is declared Internal. It is decorated with various annotations. In particular is the [DisplayName("My Display Name")] annotation. I have some code that will retrieve the value but only works if the class is declared public. I am sort of new to using reflection. I believe I need to specify that the BindingFlags.NonPublic be used but I am not sure where.
LinqPAD code:
void Main()
{
List<SpGetProfileInfoResult> p = new List<SpGetProfileInfoResult>();
p.Add(new SpGetProfileInfoResult() { FName = "Eric" });
p.Add(new SpGetProfileInfoResult() { FName = "Mike" });
p.Dump();
foreach (var item in p)
{
Console.WriteLine(item.DisplayName(i => i.FName));
Console.WriteLine(item.FName);
}
}
public partial class SpGetProfileInfoResult
{
// Uncomment this annotation to see that this part will work
// [System.ComponentModel.DisplayNameAttribute("[BILLTO-FNAME]")]
public string FName { get; set; }
}
public partial class SpGetProfileInfoResult
{
internal class Metadata
{
// This attribute is never available seems.
[System.ComponentModel.DisplayNameAttribute("[BILL-FNAME]")]
public string FName { get; set; }
}
}
public static class Tag
{
public static T GetAttribute<T>(this MemberInfo member, bool isRequired) where T : Attribute
{
var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault();
if (attribute == null && isRequired)
{
throw new ArgumentException(
string.Format(
"The {0} attribute must be defined on member {1}",
typeof(T).Name,
member.Name));
}
return (T)attribute;
}
public static string DisplayName<T>(this T src,Expression<Func<T, object>> propertyExpression)
{
Type metadata = null;
var memberInfo = GetPropertyInformation(propertyExpression.Body);
if (memberInfo == null)
{
throw new ArgumentException(
"No property reference expression was found.",
"propertyExpression");
}
var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false);
if (attr == null)
{
return memberInfo.Name;
}
return attr.DisplayName;
}
public static MemberInfo GetPropertyInformation(Expression propertyExpression)
{
MemberExpression memberExpr = propertyExpression as MemberExpression;
if (memberExpr == null)
{
UnaryExpression unaryExpr = propertyExpression as UnaryExpression;
if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Convert)
{
memberExpr = unaryExpr.Operand as MemberExpression;
}
}
if (memberExpr != null && memberExpr.Member.MemberType == MemberTypes.Property)
{
return memberExpr.Member;
}
return null;
}
}
Usage:
If you don't have LinqPAD, you should download it then you can test this pretty easily by just creating a new C# Program in LinkPAD
Debug.WriteLine(item.DisplayName(i => i.FName));
So it looks like you want to be able to decorate existing members of a partial class, by providing metadata in a separate partial piece. There's no built-in mechanism for that (see eg this question and the classes mentioned in the answer), but if you're willing to stick to a convention, you can roll your own:
So suppose we have
public partial class SpGetProfileInfoResult
{
public string FName { get; set; }
}
in a partial piece we can't change, and
public partial class SpGetProfileInfoResult
{
internal class Metadata
{
[System.ComponentModel.DisplayNameAttribute("[BILL-FNAME]")]
public string FName { get; set; }
}
}
in a partial piece we can change. You already have most of the pieces: in DisplayName(), you successfully determine that we are looking at the FName property; you then look for a DisplayNameAttribute on T.FName, but there isn't one, so that's where it stops.
What you need to do is, in the case where you don't find the attribute you need,
var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false);
if (attr == null)
{
Look for a nested class named Metadata - note here is one place we use BindingFlags.NonPublic
// Try and get a nested metadata class
var metadataType = typeof(T)
.GetNestedType("Metadata",
BindingFlags.Public | BindingFlags.NonPublic);
If we find one:
if (metadataType != null)
{
Look for a member of the same name as was originally being talked about (BindingFlags.NonPublic again)
var membersOnMetadataType = metadataType.GetMember(memberInfo.Name,
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic);
If there is one, use your helper method, but this time pass it the metadata type's member:
if (membersOnMetadataType.Any())
{
var attrOnMetadataType = membersOnMetadataType[0]
.GetAttribute<DisplayNameAttribute>(false);
return attrOnMetadataType.DisplayName;
(I've omitted a final nullity check here, as well as closing the control flow)
Depending on how distasteful you find that "Metadata" string, you could instead do something declarative with attributes:
have a class-level attribute that goes on SpGetProfileInfoResult (the piece you can change) that points at its Metadata using typeof (this is the approach taken by System.ComponentModel), or
have a class-level attribute that goes on Metadata, to have it claim 'I am a metadata type'. Then instead of searching for a nested class named a fixed string, we would instead search for a nested class having this particular attribute.
After working on this for a while I came up with a Hack. I am sure someone out there can help me clean this up a bit, but this is what I found works. I had to add the "Metadata" nested class to the DeclaringType and then do a GetMember on that result, which returns a collection of members.
public static string DisplayName<T>(this T src, Expression<Func<T, object>> propertyExpression)
{
var memberInfo = GetPropertyInformation(propertyExpression.Body);
var mytype = src.GetType();
string strType = mytype.Name + "+Metadata";
var metaType = Type.GetType(strType);
MemberInfo[] mem = metaType.GetMember(memberInfo.Name);
var att = mem[0].GetCustomAttributes(typeof(DisplayNameAttribute), true).FirstOrDefault() as DisplayNameAttribute;
if (att == null)
return memberInfo.Name;
else
return att.DisplayName;
}
I won't try to debug your code because you're using some classes that I'm not familiar with.
One thing I do know is that MemberInfo does not have a GetAttribute() function. You must be using an extension method there.
However I can tell you that you don't need any special bindingflags just because the type is internal. Only the visibility of the member is important, and in this case it's public.
using System;
using System.ComponentModel;
namespace ConsoleApplication1
{
internal class Metadata
{
[DisplayName("[BILL-FNAME]")]
public string FName { get; set; }
}
class Program
{
static void Main()
{
var memberInfo = typeof(Metadata).GetMember("FName")[0];
var atrributes = memberInfo.GetCustomAttributes(false);
Console.WriteLine(atrributes[0].GetType().Name);
}
}
}
Output:
DisplayNameAttribute
This is a scenario created to help understand what Im trying to achieve.
I am trying to create a method that returns the specified property of a generic object
e.g.
public object getValue<TModel>(TModel item, string propertyName) where TModel : class{
PropertyInfo p = typeof(TModel).GetProperty(propertyName);
return p.GetValue(item, null);
}
The code above works fine if you're looking for a property on the TModel item
e.g.
string customerName = getValue<Customer>(customer, "name");
However, if you want to find out what the customer's group's name is, it becomes a problem:
e.g.
string customerGroupName = getValue<Customer>(customer, "Group.name");
Hoping someone can give me some insight on this way out scenario - thanks.
Here's a simple method that uses recursion to solve your problem. It allows you to traverse an object graph by passing a "dotted" property name. It works with properties as well as fields.
static class PropertyInspector
{
public static object GetObjectProperty(object item,string property)
{
if (item == null)
return null;
int dotIdx = property.IndexOf('.');
if (dotIdx > 0)
{
object obj = GetObjectProperty(item,property.Substring(0,dotIdx));
return GetObjectProperty(obj,property.Substring(dotIdx+1));
}
PropertyInfo propInfo = null;
Type objectType = item.GetType();
while (propInfo == null && objectType != null)
{
propInfo = objectType.GetProperty(property,
BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.DeclaredOnly);
objectType = objectType.BaseType;
}
if (propInfo != null)
return propInfo.GetValue(item, null);
FieldInfo fieldInfo = item.GetType().GetField(property,
BindingFlags.Public | BindingFlags.Instance);
if (fieldInfo != null)
return fieldInfo.GetValue(item);
return null;
}
}
Example:
class Person
{
public string Name { get; set; }
public City City { get; set; }
}
class City
{
public string Name { get; set; }
public string ZipCode { get; set; }
}
Person person = GetPerson(id);
Console.WriteLine("Person name = {0}",
PropertyInspector.GetObjectProperty(person,"Name"));
Console.WriteLine("Person city = {0}",
PropertyInspector.GetObjectProperty(person,"City.Name"));
In the System.Web.UI namespace there's a method to do that:
DataBinder.Eval(source, expression);
I'm guessing you just need to break this down into a couple of steps rather than trying to do it all in one, something like:
// First get the customer group Property...
CustomerGroup customerGroup = getValue<Customer>(customer, "Group");
// Then get the name of the group...
if(customerGroup != null)
{
string customerGroupName = getValue<CustomerGroup>(customerGroup, "name");
}
Since Group is the property of the customer, which itself hosts the property name, you have to go this way too.
But since '.' cant be part of the name of the property you can easyly use String.Substring to remove the first property name from string and call your method recursively.
Yesterday I ran into an Issue while developing a Web Part (This question is not about webpart but about C#). Little background about the Issue. I have a code that load the WebPart using the Reflection, In which I got the AmbiguousMatchException. To reproduce it try the below code
public class TypeA
{
public virtual int Height { get; set; }
}
public class TypeB : TypeA
{
public String Height { get; set; }
}
public class Class1 : TypeB
{
}
Assembly oAssemblyCurrent = Assembly.GetExecutingAssembly();
Type oType2 = oAssemblyCurrent.GetType("AmbigousMatchReflection.Class1");
PropertyInfo oPropertyInfo2 = oType2.GetProperty("Height");//Throws AmbiguousMatchException
oPropertyInfo2 = oType2.GetProperty("Height",
BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance); // I tried this code Neither these BindingFlags or any other didnt help
I wanted to know the BindingFlag to Fetch the Height Property. You will have the question of why I wanted to create another Height Property that is already there in the Base class. That is how the Microsoft.SharePoint.WebPartPages.PageViewerWebPart was designed check the Height property of the PageViewerWebPart class.
There are two Height properties there, and neither of them are declared by Class1 which you're calling GetProperty on.
Now, would it be fair to say you're looking for "the Height property declared as far down the type hiearchy as possible"? If so, here's some code to find it:
using System;
using System.Diagnostics;
using System.Reflection;
public class TypeA
{
public virtual int Height { get; set; }
}
public class TypeB : TypeA
{
public new String Height { get; set; }
}
public class Class1 : TypeB
{
}
class Test
{
static void Main()
{
Type type = typeof(Class1);
Console.WriteLine(GetLowestProperty(type, "Height").DeclaringType);
}
static PropertyInfo GetLowestProperty(Type type, string name)
{
while (type != null)
{
var property = type.GetProperty(name, BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance);
if (property != null)
{
return property;
}
type = type.BaseType;
}
return null;
}
}
Note that if you know the return types will be different, it may be worth simplifying the code as shown in sambo99's answer. That would make it quite brittle though - changing the return type later could then cause bugs which would only be found at execution time. Ouch. I'd say that by the time you've done this you're in a brittle situation anyway :)
See the following example:
class Foo {
public float Height { get; set; }
}
class Bar : Foo {
public int Height { get; set; }
}
class BarBar : Bar { }
class Foo2 : Foo{
public float Height { get; set; }
}
class BarBar2 : Foo2 { }
static void Main(string[] args) {
// works
var p = typeof(BarBar).GetProperty("Height", typeof(float), Type.EmptyTypes);
// works
var p2 = typeof(BarBar).BaseType.GetProperty("Height", BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
// works
var p3 = typeof(BarBar2).GetProperty("Height");
// fails
var p4 = typeof(BarBar).GetProperty("Height");
Console.WriteLine(p);
}
You get an AmbiguousMatchException if a two or more properties with the differing return types and the same name live in your inheritance chain.
Stuff resolves just fine if you override an implementation (using new or override) and maintain the return type.
You can force reflection only to look at the properties for a particular type.
Obviously there are two properties that match the name you have given of "Height", one with return type int, and another string., Just add the return type as the second parameter tot the GetPropertyCall depending on which you want returning and this ambiguity should disappear.
I created two extension methods extending on Jon Skeet's answer. You can place those in any public static class.
Edit: Removed the MissingMemberException to behave more like the default .NET implementations which return null on failure.
Usage:
var field = type.GetFieldUnambiguous(type, "FieldName", bindingFlags);
var property = type.GetPropertyUnambiguous(type, "PropertyName", bindingFlags);
Implementation:
public static FieldInfo GetFieldUnambiguous(this Type type, string name, BindingFlags flags)
{
if (type == null) throw new ArgumentNullException(nameof(type));
if (name == null) throw new ArgumentNullException(nameof(name));
flags |= BindingFlags.DeclaredOnly;
while (type != null)
{
var field = type.GetField(name, flags);
if (field != null)
{
return field;
}
type = type.BaseType;
}
return null;
}
public static PropertyInfo GetPropertyUnambiguous(this Type type, string name, BindingFlags flags
{
if (type == null) throw new ArgumentNullException(nameof(type));
if (name == null) throw new ArgumentNullException(nameof(name));
flags |= BindingFlags.DeclaredOnly;
while (type != null)
{
var property = type.GetProperty(name, flags);
if (property != null)
{
return property;
}
type = type.BaseType;
}
return null;
}