Metacrawler
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
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
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.
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.
ASKER
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?
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?
ASKER
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*
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);
}
}
}
Let me address a couple things separately...
You can simplify your AddReferencedAssemblies() method a little...
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.
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);
}
}
>> 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.
ASKER
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!
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!
ASKER
A question the code you provided to enumerate referenced assemblies...
Why did you start getting referenced assemblies at the assembly returned by Assembly.GetExecutingAssem bly()? 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?
Why did you start getting referenced assemblies at the assembly returned by Assembly.GetExecutingAssem
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");
}
}
When you could just write:
yourSerializer1.YourMethod();
yourSerializer2.YourMethod();
yourSerializer3.YourMethod();
// etc...
yourSerializer_n.YourMethod();
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.TheirMeth
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.GetExecutingAssem bly()?
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.
ASKER
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.TheirMethYes, 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.od()" everytime they add a serializer. Are either of those the case?
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".
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.
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* ;-)
ASKER
Ok, second attempt..
What do you think... Will that do the job, or will that solution have other drawback?
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);
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hello tgerbert
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 :-)
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.
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...
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