The approach we have taken is as follows.
Create an http module called SharedAssemblyLauncher. This assembly will be released to all teams and they have to define this module in their web.config.
The root web.config will define a key "SharedAssemblyBaseDirectory" to specify the base shared assembly location from where the probing starts. Each application web.config can override this too. For supporting multiple versions, you can create folders with the version number and then copy the assemblies to that folder. The probling rule is as follows
1. Look under the folder with version number.
2. Look under folder Common.
3. Base directory
We start with creating a new handler for AssemblyResolve for current domain.
public class SharedAssemblyLauncher : IHttpModule { private static string _BaseAssemblyFolder = ""; static SharedAssemblyLauncher() { if (HttpContext.Current != null) { string appRoot = HttpContext.Current.Server.MapPath("~/"); string webRoot = HttpContext.Current.Server.MapPath("/"); if (appRoot != webRoot) _BaseAssemblyFolder = GetSharedAssemblyBaseDirectoryFromConfigFile(GetSlashEndedFolder(appRoot) + "web.config"); if (_BaseAssemblyFolder.Trim() == "") _BaseAssemblyFolder = GetSharedAssemblyBaseDirectoryFromConfigFile(GetSlashEndedFolder(webRoot) + "web.config"); } AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); } public void Dispose() { } public void Init(HttpApplication context) { } }
In assembly resolve handler, the first thing what we will do is loop through the loaded assemblies to look for a match. We've noticed that, in case for a request for embedded resource the version number was not passed, only the assembly name is passed. So if we probe, we might not find any matching assembly. So it is very important to loop through and find a match before the actual probing starts. If no match found on loaded assemblies, probe based on the sequence defined earlier. If no match found while probing, return null so that the framework does its own probing.
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in loadedAssemblies) { if (assembly.FullName == args.Name || assembly.FullName.Contains(args.Name)) return assembly; } if (_BaseAssemblyFolder.Trim() == "") return null; string assemblyFileName = GetAssemblyFileName(args.Name); string assemblyVersion = GetAssemblyVersion(args.Name); try { string fileName = string.Format("{0}{1}\\{2}.dll", _BaseAssemblyFolder, assemblyVersion, assemblyFileName); if (File.Exists(fileName)) return GetAssemblyFromPath(fileName); fileName = string.Format("{0}Common\\{1}.dll", _BaseAssemblyFolder, assemblyFileName); if (File.Exists(fileName)) return GetAssemblyFromPath(fileName); fileName = string.Format("{0}{1}.dll", _BaseAssemblyFolder, assemblyFileName); if (File.Exists(fileName)) return GetAssemblyFromPath(fileName); } catch { } return null; } private static string GetSlashEndedFolder(string baseFolder) { return (baseFolder.Trim().EndsWith("\\") ? baseFolder.Trim() : baseFolder.Trim() + "\\"); } private static string GetSharedAssemblyBaseDirectoryFromConfigFile(string configPath) { XmlDocument configXmlDoc = new XmlDocument(); if (!File.Exists(configPath)) return ""; configXmlDoc.Load(configPath); XmlNode baseDirectoryNode = configXmlDoc.SelectSingleNode("/configuration/appSettings/add[@key=\"SharedAssemblyBaseDirectory\"]"); if (baseDirectoryNode == null) return ""; return baseDirectoryNode.Attributes["value"].Value; } private static string GetAssemblyFileName(string assemblyName) { return assemblyName.Split(",".ToCharArray())[0]; } private static string GetAssemblyVersion(string assemblyName) { string[] parts = assemblyName.Split(",".ToCharArray()); for (int i = 0; i < parts.Length; i++) { if (parts[i].Trim().StartsWith("Version=")) { string[] versionParts = parts[i].Split("=".ToCharArray()); return versionParts[1].Trim(); } } return ""; } private static Assembly GetAssemblyFromPath(string assemblyPath) { return Assembly.LoadFile(assemblyPath); }
One important thing to remeber here is that never try to read configuration values using ConfigurationManager in this module.
Happy sharing!!!
No comments:
Post a Comment