How to Get the Member to Which My Custom Attribute Was Applied

How do I get the member to which my custom attribute was applied?

Since there seems to be a lot of confusion with respect to how the stack frames and methods work, here is a simple demonstration:

static void Main(string[] args)
{
MyClass c = new MyClass();
c.Name = "MyTest";
Console.ReadLine();
}

class MyClass
{
private string name;

void TestMethod()
{
StackTrace st = new StackTrace();
StackFrame currentFrame = st.GetFrame(1);
MethodBase method = currentFrame.GetMethod();
Console.WriteLine(method.Name);
}

public string Name
{
get { return name; }
set
{
TestMethod();
name = value;
}
}
}

The output of this program will be:

set_Name

Properties in C# are a form of syntactic sugar. They compile down to getter and setter methods in the IL, and it's possible that some .NET languages might not even recognize them as properties - property resolution is done entirely by convention, there aren't really any rules in the IL spec.

Now, let's say for the moment that you had a really good reason for a program to want to examine its own stack (and there are precious few practical reasons to do so). Why in the world would you want it to behave differently for properties and methods?

The whole rationale behind attributes is that they are a kind of metadata. If you want a different behaviour, code it into the attribute. If an attribute can mean two different things depending on whether it's applied to a method or property - then you should have two attributes. Set the target on the first to AttributeTargets.Method and the second to AttributeTargets.Property. Simple.

But once again, walking your own stack to pick up some attributes from the calling method is dangerous at best. In a way, you are freezing your program's design, making it far more difficult for anybody to extend or refactor. This is not the way attributes are normally used. A more appropriate example, would be something like a validation attribute:

public class Customer
{
[Required]
public string Name { get; set; }
}

Then your validator code, which knows nothing about the actual entity being passed in, can do this:

public void Validate(object o)
{
Type t = o.GetType();
foreach (var prop in
t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (Attribute.IsDefined(prop, typeof(RequiredAttribute)))
{
object value = prop.GetValue(o, null);
if (value == null)
throw new RequiredFieldException(prop.Name);
}
}
}

In other words, you're examining the attributes of an instance that was given to you but which you don't necessarily know anything about the type of. XML attributes, Data Contract attributes, even Attribute attributes - almost all attributes in the .NET Framework are used this way, to implement some functionality that is dynamic with respect to the type of an instance but not with respect to the state of the program or what happens to be on the stack. It is very unlikely that you are actually in control of this at the point where you create the stack trace.

So I'm going to recommend again that you don't use the stack-walking approach unless you have an extremely good reason to do so which you haven't told us about yet. Otherwise you are likely to find yourself in a world of hurt.

If you absolutely must (don't say we didn't warn you), then use two attributes, one that can apply to methods and one that can apply to properties. I think you'll find that to be much easier to work with than a single super-attribute.

Get member to which attribute was applied from inside attribute constructor?

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.

Getting the class which one of its members using custom attribute

You can not get any info about the class which contains members which are decorated by some attribute, inside the attributes constructor, as I already pointed out in my previous answer here.

Instance to Attribute

But i suggested a solution, which is to invoke a method inside your attribute instead of using the constructor, and this will basically get you the same result.

I have reworked my previous answer a bit to solve your problem in the following way.

Your attribute will now need to use the following method instead of the constructor.

[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
public void MyAttributeInvoke(object source)
{
source.GetType()
.GetProperties()
.ToList()
.ForEach(x => Console.WriteLine(x.Name));
}
}

And your Test class will need to have the following piece of code inside its constructor.

public class Test
{
public Test()
{
GetType().GetMethods()
.Where(x => x.GetCustomAttributes(true).OfType<MyAttribute>().Any())
.ToList()
.ForEach(x => (x.GetCustomAttributes(true).OfType<MyAttribute>().First() as MyAttribute).MyAttributeInvoke(this));
}

[MyAttribute]
public void MyMethod() { }

public string Name { get; set; }

public string LastName { get; set; }
}

By running the following lines of code, you will notice that you can access both properties from the Test class from your attribute.

class Program
{
static void Main()
{
new Test();

Console.Read();
}
}

Check user access with custom attributes

So the other guys are right, that the attributes do nothing by themselves. It is just metadata that you have to purposely get at some point during the lifetime of the service call.

The best way to do that so it is sort of done auto-magically and not always directly in every operation is to add inspectors and service behaviors. It is more work to setup initially, but it gets that out of your direct operation code and can make it apply for any operation to check for that custom attribute.

Basically you have your attribute like so:

namespace MyCustomExtensionService
{
public class UserAccessAttribute : System.Attribute
{
private string _userRole;

public UserAccessAttribute(string userRole)
{
_userRole = userRole;

//you could also put your role validation code in here

}

public string GetUserRole()
{
return _userRole;
}
}
}

Then you set up your parameter inspector (note there are other inspectors you could use):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Web;

namespace MyCustomExtensionService
{
public class MyParameterInspector : IParameterInspector
{

public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
{
//throw new NotImplementedException();
}

public object BeforeCall(string operationName, object[] inputs)
{
MethodInfo method = typeof(Service1).GetMethod(operationName);
Attribute[] attributes = Attribute.GetCustomAttributes(method, typeof(UserAccessAttribute), true);

var attr = (UserAccessAttribute)attributes.First();

if (attributes.Any())
{
var userHasProperAuthorization = true;
if (attr.GetUserRole() == "Residents" && userHasProperAuthorization)
{
//everything is good, continue to operation
}
else
{
throw new FaultException("You do not have the right security role!");
}
}



return null;

}
}
}

Then you setup your endpoint behavior (there are other behaviors you could use):

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Web;

namespace MyCustomExtensionService
{
public class MyCustomAttributeBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
//throw new NotImplementedException();
}

public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
foreach (ClientOperation clientOperation in clientRuntime.Operations)
{
clientOperation.ParameterInspectors.Add(
new MyParameterInspector());
}
}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
foreach (DispatchOperation dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
{

dispatchOperation.ParameterInspectors.Add(
new MyParameterInspector());
}
}

public void Validate(ServiceEndpoint endpoint)
{
//throw new NotImplementedException();
}
}
}

Then you create your behavior section:

using System.Linq;
using System.ServiceModel.Configuration;
using System.Web;

namespace MyCustomExtensionService
{
public class MyBehaviorSection : BehaviorExtensionElement
{

protected override object CreateBehavior()
{
return new MyCustomAttributeBehavior();

}

public override Type BehaviorType
{

get { return typeof(MyCustomAttributeBehavior); }

}
}
}

Then you setup the config to use the new behavior:

<system.serviceModel>
<services>
<service name ="MyCustomExtensionService.Service1">
<endpoint address="" behaviorConfiguration="MyCustomAttributeBehavior"
binding="basicHttpBinding" contract="MyCustomExtensionService.IService1">
</endpoint>
</service>
</services>
<extensions>
<behaviorExtensions>
<add name="Validator" type="MyCustomExtensionService.MyBehaviorSection, MyCustomExtensionService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="MyCustomAttributeBehavior">
<Validator />
</behavior>
</endpointBehaviors>

here is the services interface - with one operation that will work and one that will fail due to having the wrong user access

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace MyCustomExtensionService
{

[ServiceContract]
public interface IService1
{

[OperationContract]
string GetData(int value);

[OperationContract]
string GetDataUsingWrongUserAccess(int value);

}


}

And the service operations:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace MyCustomExtensionService
{

public class Service1 : IService1
{
[UserAccess("Residents")]
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}

[UserAccess("Admin")]
public string GetDataUsingWrongUserAccess(int value)
{
return string.Format("You entered: {0}", value);
}
}
}

For more info, see MSDN http://msdn.microsoft.com/en-us/library/ms730137.aspx

also for inspectors:https://github.com/geersch/WcfParameterInspectors

How to get name of property which our attribute is set?

Attributes are metadata applied to members of a type, the type itself, method parameters, or the assembly. For you to have access to the metadata, you must have had the original member itself to user GetCustomAttributes etc, i.e. your instance of Type, PropertyInfo, FieldInfo etc.

In your case, I would actually pass the name of the property to the attribute itself:

public CustomAttribute : Attribute
{
public CustomAttribute(string propertyName)
{
this.PropertyName = propertyName;
}

public string PropertyName { get; private set; }
}

public class MyClass
{
[Custom("MyProperty")]
public int MyProperty { get; set; }
}

Accessing a custom attribute from within an instanced object

Attributes aren't used that way. Attributes are stored in the class object that contains the attribute, not the member object/field that is declared within the class.

Since you want this to be unique per member, it feels more like this should be member data with MyField, something passed in by the constructor.

If you're hell bent on using an attribute, you could add code in your constructor that used reflection on every member that has an attribute attached to it an attempts to set its instance data to what you want, but you would need to make sure that all your MyField objects are fully constructed.

Alternately you could make your setter look like this:

private MyField _first;
[MyAttribute("This is the first")]
public MyField First {
get { return _first; }
set {
_first = value;
if (_first != null) {
_first.SomeAttribute = GetMyAttributeValue("First");
}
}
}

private string GetMyAttributeValue(string propName)
{
PropertyInfo pi = this.GetType().GetPropertyInfo(propName);
if (pi == null) return null;
Object[] attrs = pi.GetCustomAttributes(typeof(MyAttribute));
MyAttribute attr = attrs.Length > 0 ? attrs[0] as MyAttribute : null;
return attr != null ? attr.Value : null;
}

Get property value to use within custom attribute

OK, I figured it out. This is only available with .Net 4.5 and later though.

In the System.Runtime.CompilerServices library, there is a CallerMemberNameAttribute class available. To get the name of the calling class, there is a CallerFilePathAttribute class that returns the full path of the calling class for later use with Reflection. Both are used as follows:

public class CustomAttribute1: Attribute
{
public CustomAttribute1([CallerMemberName] string propertyName = null, [CallerFilePath] string filePath = null)
{
//Returns "Name"
Console.WriteLine(propertyName);

//Returns full path of the calling class
Console.WriteLine(filePath);
}
}

I hope you find this as helpful in your work.

C# Custom Attribute code - Look at the field that it was associated with

The solution could not be solved directly, as @Mario pointed out, but here is the solution I ended up going with.

The key is to know that the attribute alone has no way of knowing this information, but at the time it is called it is reasonable to expect that the FieldInfo or PropertyInfo was also available.

My original problem was that my ORM code looked to an attribute to determine if a field/property related to a database field. Then, I had instances where the Prop/Field name in the class did not match up with the database for reasons of making it more logical to the Code/Db. In those cases I needed to pass in a field name to use instead of the actual field. I was hoping the attribute could do more of the work, or at least help make it more obvious for any future code that used it.

(I stripped out xml comments and extra code not relavant to this solution)

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class DbFieldAttribute : Attribute
{
private string fieldName = "";

public DbFieldAttribute() { }

public DbFieldAttribute(string fieldName)
{
this.fieldName = fieldName;
}

public string FieldName(PropertyInfo pi)
{
if (this.fieldName != "") return this.fieldName;
else return pi.Name;
}

public string FieldName(FieldInfo fi)
{
if (this.fieldName != "") return this.fieldName;
else return fi.Name;
}

Now when my ORM code wants the field name, it has to pass in the field or property info related to that field. This means that what is needed, is now intrinsic in the attributes use, instead of needing to be derived in external code.

How do I retrieve a custom attribute value from a property reference.

You can do this to get the XPathAttribute associated with a property:

var attr = (XPathAttribute)typeof(Appointment)
.GetProperty("Id")
.GetCustomAttributes(typeof(XPathAttribute), true)[0];

You could wrap this in a method using an Expression like this:

public static string GetXPath<T>(Expression<Func<T>> expr)
{
    var me = expr.Body as MemberExpression;
    if (me != null)
    {
        var attr = (XPathAttribute[])me.Member.GetCustomAttributes(typeof(XPathAttribute), true);
        if (attr.Length > 0)
        {
            return attr[0].Value;
        }
    }
    return string.Empty;
}

And call it like this:

Appointment appointment = new Appointment();
GetXPath(() => appointment.Id) // appt/@id

Or if you prefer to be able to call this without having an object instance to reference:

public static string GetXPath<T, TProp>(Expression<Func<T, TProp>> expr)
{
    var me = expr.Body as MemberExpression;
    if (me != null)
    {
        var attr = (XPathAttribute[])me.Member.GetCustomAttributes(typeof(XPathAttribute), true);
        if (attr.Length > 0)
        {
            return attr[0].Value;
        }
    }
    return string.Empty;
}

And call it like this:

GetXPath<Appointment>(x => x.Id); // appt/@id

How to find all occurrences of a custom attribute in assemblies?

Do,

assembly.GetTypes()
.SelectMany(type => type.GetMembers())
.Union(assembly.GetTypes())
.Where(type => Attribute.IsDefined(type, attributeType));

This will return enum values too since those are just public static fields under the hood. Also, if you want private members, you'll have to tweak the BindingFlags you pass in.



Related Topics



Leave a reply



Submit