In my previous post I pointed out how to implement the Jeffrey Richter alternative to ILmerge on WPF applications. Now I want to show you how to do this with class libraries using module initializers tool from Einar Egilsson.
Again the idea is to add all referenced assemblies as embedded resources and load them when they are needed. But a class library has no constructor or initializer in the world of C#. Where to register the AssemblyResolve eventhandler? According to Einar Egilsson’s post you can add a module initializer by injecting IL code into the assembly. The framework supports module initializers, which are called before any other code from the module (assembly) is executed, when the assembly is hit for the first time.
First add the assemblies from the references as embedded resources (see previous post for details)
Then create a public parameterless static void method. This will be called from the module initializer,
/// <summary> /// Run on first construction of this module /// </summary> public static void Run() { AppDomain.CurrentDomain.AssemblyResolve += (s, a) => { // construct resource key from type name and namespace var name = new System.Reflection.AssemblyName(a.Name).Name; var resourceName = string.Format("YOUR_NAMESPACE.Resources.{0}.dll", name); // lookup the embedded resource for the requested type using (var stream = System.Reflection.Assembly.GetAssembly(typeof(YOUR_CLASSNAME)) .GetManifestResourceStream(resourceName)) { if (stream != null) { // load assembly from embedded resource var assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return System.Reflection.Assembly.Load(assemblyData); } else { // Embedded resource not found System.Diagnostics.Trace.TraceError("Unable to locate resource {0}", resourceName); return null; } } }; }
Notice the YOUR_NAMESPACE and the Resources after that. The assemblies are in the Resources folder and that will be part of the name of the embedded resource. Different from my previous post load the assembly containing YOUR_CLASSNAME type as it will not be the ExecutingAssembly.
TIP: add a console application with tracelistener to you solution to figure out the unresolved resources
Add a postbuild step to the project that injects the module initializer. You can download InjectModuleInitializer from Einar Egilsson‘s site.
<Target Name="AfterBuild"> <Exec Command='InjectModuleInitializer.exe /m:YOUR_NAMESPACE.YOUR_CLASSNAME::Run "$(TargetPath)"' /> </Target>
I was able to use this in an ASP.NET webapplication. Zip file with solution and projects download. Thumbs up!