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
How to Change the Background Color of All Other Forms from One Form
Convert String to Hex-String in C#
Mvvm Light 5.0: How to Use the Navigation Service
What Is the Fastest Way to Create a Checksum for Large Files in C#
How to Run and Interact with an Async Task from a Wpf Gui
Identityserver4 Register Userservice and Get Users from Database in ASP.NET Core
Dynamic Routes from Database for ASP.NET MVC Cms
How to Initialize a C# Dictionary with Values
How to Get the Full Url of the Page I am on in C#
Why Func<T,Bool> Instead of Predicate<T>
What's the Difference Between Returning Void and Returning a Task
How Can a Separator Be Added Between Items in an Itemscontrol
How to Monitor Clipboard Content Changes in C#
JSONserializersettings and ASP.NET Core
How to Get the Current User Directory
String Interning in .Net Framework - What Are the Benefits and When to Use Interning