Solved

How to execute attribute code at startup?

Posted on 2011-02-16
13
434 Views
Last Modified: 2012-08-13
Hello Community,

I've written a custom attribute class that can be used to annotate classes. Now I need a technique to "collect" classes annotated with that attribute when starting up. At best before the main method runs.

Background: The attribute marks classes that provide overriders for my serialization mechanism and the serialization mechanism needs to know available overriders. I would like to register these overriders automatically with the serialization mechanism.

Is that possible? If yes, how can I achieve that?

Thank you in advance!

Greetings,
Metacrawler
0
Comment
Question by:Metacrawler
  • 8
  • 5
13 Comments
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 34907226
You can use Reflection to inspect types in a given assembly, finding which ones have a particular custom attribute.  This example looks for classes marked with [Serializable], but it should apply to any attribute you want.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication1
{
	class Program
	{
		static void Main(string[] args)
		{
			Assembly thisAssembly = Assembly.GetExecutingAssembly();

			foreach (Type t in thisAssembly.GetTypes())
			{
				if (t.GetCustomAttributes(true).OfType<SerializableAttribute>().Count() > 0)
					Console.WriteLine(t.Name + " has Serializable attribute.");
			}

			Console.ReadKey();
		}
	}
}

Open in new window

0
 

Author Comment

by:Metacrawler
ID: 34913701
Hello tgerbert,

thank you for your response. Ok, that's right... but that does not solve my problem :-)

I'm looking for a way how I can execute code for every annotated class (e.g. I create class A and annotate it with MyAttribute. Now I want some code to be executed for this annotation. If I create class B and annotate it with MyAttribute, I also want to execute that code, once for each annotated class. If I remember rightly, that is called aspect oriented programming (AOP), but I'm not sure...)

I suppose that is a problem in C#, since the AOP approach I know uses .NET Remoting even when not communication at all, which results in a very slow implementation.
0
 

Author Comment

by:Metacrawler
ID: 34913716
Oh, I'm a blockhead ;-)

I could execute your code in the static constructor of the Serializer class. So it will only run once, inspect all classes and find my overrider classes. The only drawback here is, that I can inspect only classes that are in already loaded assemblies. Is there a way to load all referenced assemblies before starting inspection?
0
 

Author Comment

by:Metacrawler
ID: 34914497
Hmm, I've to clarify my last post a little bit...

I've managed to get all referenced assemblies using the attached code. But referencing seems to be a bit tricky... If I do not use a type from an assembly the assembly reference is optimized away by the compiler, so it is not referenced any more... But when my serializer deserializes a stream of bytes to a concrete object, it needs to know these types, otherwise it cannot deserialize them... *headache*
...
List<Assembly> referencedAssemblies = new List<Assembly>();
AddReferencedAssemblies(Assembly.GetEntryAssembly(), referencedAssemblies);
...

private static void AddReferencedAssemblies(Assembly assembly, List<Assembly> assemblies)
{
	assemblies.Add(assembly);
	AssemblyName[] names = assembly.GetReferencedAssemblies();
	foreach (AssemblyName name in names)
	{
		bool bProceed = true;
		foreach (Assembly other in assemblies) {
			AssemblyName otherName = other.GetName();
			if (name.FullName == otherName.FullName) {
				bProceed = false;
				break;
			}
		}
				
		if (bProceed) {
			Assembly child = Assembly.Load(name);
			AddReferencedAssemblies(child, assemblies);
		}				
	}
}

Open in new window

0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 34918006
Let me address a couple things separately...

You can simplify your AddReferencedAssemblies() method a little...
List<Assembly> allAssemblies = new List<Assembly>();
GetAllAssemblies(Assembly.GetExecutingAssembly(), allAssemblies);

static void GetAllAssemblies(Assembly assembly, List<Assembly> assemblies)
{
	if (!assemblies.Contains(assembly))
	{
		assemblies.Add(assembly);
		foreach (AssemblyName referencedAssemblyName in assembly.GetReferencedAssemblies())
			GetAllAssemblies(Assembly.Load(referencedAssemblyName), assemblies);
	}
}

Open in new window


>> best before the main method runs
Not much runs before Main() - probably the only thing guaranteed to run before Main() would be the static constructor of the class containing Main() (usually "Program").  However, Main() seems to me like it would probably be the more appropriate place.

>> I can inspect only classes that are in already loaded assemblies
The compiler ignores references to assemblies that are never used.  You can add a method to the child assemblies that does nothing and call it from your main application, this way the compiler won't optimize it away.  To my knowledge there is no option to alter this behavior in Visual Studio.

Now that I'm grasping what you're trying to accomplish a little better - I think I might suggest using interfaces instead of decorating classes and methods with attributes, if you're agreeable to it.  In my mind your simplest setup would be your application, a class library containing interfaces, and one or more class libraries with the custom serializers in it.  The main application would reference the interface library, and the serializers would also reference the interface library; there would be no explicit dependancies between the main application and the serializer assemblies.  Drop the serializers in a specific sub-folder of your application, "plugins" for example, and the main application can load each DLL in that directory and inspect them for types implementing a particular interface.  For each such type in the DLL the main application would use Assembly.CreateInstance() to load the type and cast it to a IYourSerializerInterface.  This way you have a loose & flexible run-time binding.

Your current approach (if I'm understanding correctly) requires you to manage references at compile-time, which means you will have foreknowledge of all the types in your solution, almost making the use of attributes moot.  If you still want to continue with the attributes you can still leverage the design model outlined in the paragraph above, keeping the attributes in their own separate library, so the main application and the child assemblies can both reference the assembly containing the attribute.
0
 

Author Comment

by:Metacrawler
ID: 34923778
Hmm, your plugin approach is pretty flexible, but requires overrider classes to be put in separate libraries causing the solution to blow up. Our current solution consists of about 40 projects and is growing steadily, so I would like to avoid additional libraries whereever possible.

I agree with you that my approach needs to know serializable types at compile-time, but that should not be a problem, since I must know the type I'm writing an overrider class for. The only drawback I can see is that I cannot serialize types that become visible at runtime (e.g. by loading a plugin assembly). That could be a severe disadvantage...

I think, I'll consider the plugin concept a little further...

Thank you for your proficient help on this topic!
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 

Author Comment

by:Metacrawler
ID: 34923802
A question the code you provided to enumerate referenced assemblies...

Why did you start getting referenced assemblies at the assembly returned by Assembly.GetExecutingAssembly()? In my oppinion that method returns the assembly that is currently executing the code, not the entry assembly. Would Assembly.GetEntryAssembly() be a better choice or doesn't it matter?
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 34937630
I agree with you that my approach needs to know serializable types at compile-time, but that should not be a problem, since I must know the type I'm writing an overrider class for.

My point really is (and it may not be a valid point, really I'm asking a question here...):
Since you know about the serializer types at design-time why write:
foreach(Assembly assembly in GetAllReferencedAssemblies())
{
  foreach(Type t in assembly.GetTypes().Where(x => x.HasAttribute("YourCustomAttribute")))
  {
    t.InvokeMember("YourMethod");
  }
}

Open in new window

When you could just write:
yourSerializer1.YourMethod();
yourSerializer2.YourMethod();
yourSerializer3.YourMethod();
// etc...
yourSerializer_n.YourMethod();

Open in new window


I can see the first approach would make a lot of sense if the serializers were in assemblies not under your control, where you don't know the class or method names at design-time - and I suppose if multiple people were working on the same solution, and don't want to have them need to add "theirSerializer.TheirMethod()" everytime they add a serializer. Are either of those the case?


plugin approach is pretty flexible, but requires overrider classes to be put in separate libraries causing the solution to blow up
Not necessarilly; you could still use interfaces, finding all classes in the current assembly, and all referenced assemblies, that implement the interface.  Or put all the serializers together in one library.

Why did you start getting referenced assemblies at the assembly returned by Assembly.GetExecutingAssembly()?
No particular reason - in my test project that code was in Main(), so the entry and executing assemblies were the same, so it didn't matter - but you're right, you'll probably want GetEntryAssembly.
0
 

Author Comment

by:Metacrawler
ID: 34940751
I can see the first approach would make a lot of sense if the serializers were in assemblies not under your control, where you don't know the class or method names at design-time - and I suppose if multiple people were working on the same solution, and don't want to have them need to add "theirSerializer.TheirMethod()" everytime they add a serializer. Are either of those the case?
Yes, the point is that I'm writing a serializer that will be used by my colleages. I have put it into an assembly and my colleagues will reference the serializer assembly later. So I don't know about the overriders they may write, but when building the solution all types should be visible to the compiler, so my serializer can use reflection to find the overriders my colleagues wrote.


No particular reason - in my test project that code was in Main(), so the entry and executing assemblies were the same, so it didn't matter - but you're right, you'll probably want GetEntryAssembly.
I've found a reason why GetEntryAssembly() is not a good idea... When writing a test using the Visual Studio 2010 built-in test facility, the test fails, since the serializer library is loaded dynamically into the test executable. That means there is no entry assembly in that case. GetEntryAssembly() returns null and the program "crashes".

Do you have a hint for me how I can get all assemblies in that case? GetExecutingAssembly() will return - as the name says - the assembly that is executing the code and GetCallingAssembly() will do it neither, since it just returns the assembly that called the assembly enumeration code *sigh* ;-)
0
 

Author Comment

by:Metacrawler
ID: 34940822
Ok, second attempt..

What do you think... Will that do the job, or will that solution have other drawback?

List<Assembly> referencedAssemblies = new List<Assembly>();
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) {
	AddReferencedAssemblies(assembly, referencedAssemblies);
}

Open in new window

0
 
LVL 33

Accepted Solution

by:
Todd Gerbert earned 500 total points
ID: 34944444
The only down-side I see to using AppDomain.CurrentDomain.GetAssemblies() is that you're going to end up with a list of more assemblies than are really being referenced by your code, which is going to make the search for your overrider's take longer (though in reality this may not be an issue if you're only doing it once).

Here's some sample code that will find all classes marked with CustomSerializerAttribute, find all methods in that type that are marked with CustomSerializerMethodAttribute and execute them.  In reality your code will be a little more complex - e.g. you should check the return type and parameter types (if any) for the method before invoking it and throw an exception if it doesn't match what you're expecting, among things.

List<Assembly> allAssemblies = new List<Assembly>();
foreach (Assembly assmbly in AppDomain.CurrentDomain.GetAssemblies())
	GetAllAssemblies(assmbly, allAssemblies);

foreach (Assembly assmbly in allAssemblies)
{
	foreach (Type t in assmbly.GetTypes())
	{
		if (t.GetCustomAttributes(true).OfType<CustomSerializerAttribute>().Count() > 0)
		{
			// This class has CustomSerializer attribute
			// create an instance and find methods decorated
			// with CustomSerializerMethod attriubte and execute'em
			object serializer = assmbly.CreateInstance(t.FullName);

			foreach (MethodInfo method in t.GetMethods(BindingFlags.Instance | BindingFlags.Public))
			{
				if (method.GetCustomAttributes(true).OfType<CustomSerializerMethodAttribute>().Count() > 0)
				{
					method.Invoke(serializer, null);
				}
			}
		}
	}
}
// This method call does nothing, it's only purpose
// is to prevent the compiler from optimizing-away a reference
SerializerImplementation.MySerializer.Noop();

Open in new window


Another suggestion I have is to simply have your colleagues inherit from your Serializer object and override metods as they see fit - or have them "register" their serialization code by calling a method in your Serializer object and providing a "OverrideSerializationDelegate" you can invoke instead of/in addition to your object's serialization methods.
0
 

Author Comment

by:Metacrawler
ID: 34949222
Hello tgerbert

Another suggestion I have is to simply have your colleagues inherit from your Serializer object and override metods as they see fit - or have them "register" their serialization code by calling a method in your Serializer object and providing a "OverrideSerializationDelegate" you can invoke instead of/in addition to your object's serialization methods.

Open in new window


The serializer is embedded in a communication module, that just takes some object and serializes it, so the serializer is not really visible to the user. But I think, the hints you gave me will take me further.

Thank you very much! The points are yours :-)
0
 
LVL 33

Expert Comment

by:Todd Gerbert
ID: 34952941
So they pass you an object to the serializer code, and your serializer code checks for the custom attributes so that the object can override it's own serialization?

Interfaces might still make life a little easier...

interface ICustomSerialization
{
	bool CustomSerializeMethod(string stuff);
}

static void YourSerializationMethod(object objectToSerialize)
{
	if (typeof(ICustomSerialization).IsAssignableFrom(objectToSerialize.GetType()))
	{
		ICustomSerialization customSerializationObj = (ICustomSerialization)objectToSerialize;
		customSerializationObj.CustomSerializeMethod("hello world");
	}
}

Open in new window

0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Introduction                                                 Was the var keyword really only brought out to shorten your syntax? Or have the VB language guys got their way in C#? What type of variable is it? All will be revealed.   Also called…
Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

759 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now