Sunday, November 25, 2007 10:56 PM
mehfuzh
How to control C# interface behavior using attribute.
Attribute based programming , is the most cool thing in C#, you can give some extra behavior to a method or interface , along with method call parameters.
Moving forward, let's say we are doing a service call , and we need to define the external method , right from the interface.
Now, here is an interface
public interface IFlickr : IDisposable
{
// photo releated method.
[FlickrMethod("flickr.photos.getInfo")]
Photo GetPhotoDetail(string id, PhotoSize size);
....
....
....
}
As, you can see I have defined the rest method to be called along with method declaration.To make this happen, on the start of every interface implementation we need to add, for example, in LINQ.Flickr project , inside the DataAccess that implements IFlickr, looks like
Photo IFlickr.GetPhotoDetail(string id, PhotoSize size)
{
....
....
string method = Helper.GetExternalMethodName();
....
....
....
}
As, we can see that the actual magic goes to a Helper class that will extract the attribute value from the interface. Basically, on each method call, the execution of the extraction code is bit costly. Therefore, the best solution is to parse the attributes for interface when DataAccess class is initialized and store it in a key value pair dictionary.
Therefore, Inside the .ctor call of DataAccess. let's add the following lines.
public DataAccess()
{
try
{
.....
.....
Helper.RefreshExternalMethodList(typeof(IFlickr));
}
catch (Exception ex)
{
throw new ApplicationException(ex.Message);
}
}
Lets, step in to RefreshExternalMethodList and we will get
private static IDictionary<string, string> _methodList = new Dictionary<string, string>();
internal static void RefreshExternalMethodList(Type interfaceType)
{
// not yet initialized.
if (_methodList.Count == 0)
{
MethodInfo[] mInfos = interfaceType.GetMethods();
foreach (MethodInfo mInfo in mInfos)
{
if (mInfo != null)
{
object[] customArrtibute = mInfo.GetCustomAttributes(typeof(FlickrMethodAttribute), true);
if (customArrtibute != null && customArrtibute.Length == 1)
{
FlickrMethodAttribute mAtrribute = customArrtibute[0] as FlickrMethodAttribute;
string methodFullName = mInfo.ReflectedType.FullName + "." + mInfo.Name;
if (!_methodList.ContainsKey(methodFullName))
{
_methodList.Add(methodFullName, mAtrribute.MethodName);
}
}
}
}
}
}
Which does, nothing but maps the Interface method to external method. Therefore, going back to GetExternalMethodName() , all it does is, it takes the interface ref from the current stacktrace and passes the name to return the name of the external method. That looks like
private static object _lockHandler = new object();
internal static string GetExternalMethodName()
{
lock (_lockHandler)
{
StackTrace trace = new StackTrace(1, true);
MethodBase methodBase = trace.GetFrames()[0].GetMethod();
return _methodList[methodBase.Name];
}
}
Note, that StackTrace is started from 1st frame,that means , it gets frame list starting from last method, which is, the IFlickr implementation in DataAccess.
Did I miss anything, yep happy holidays and thanksgiving :-)
Filed under: C#