Link to home
Start Free TrialLog in
Avatar of Metacrawler
MetacrawlerFlag for Germany

asked on

How to execute attribute code at startup?

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
Avatar of Todd Gerbert
Todd Gerbert
Flag of United States of America image

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

Avatar of Metacrawler

ASKER

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.
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?
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

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.
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!
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?
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.
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* ;-)
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

ASKER CERTIFIED SOLUTION
Avatar of Todd Gerbert
Todd Gerbert
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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 :-)
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