Following on from my previous post here on different methods of configuring ActiveRecord, I’ve decided to go into more detail on this and more specifically into automatically loading assemblies and configuring ActiveRecord without much fuss.
Download
ARConfig.zip
Code
One of the many overloaded options for configuring ActiveRecord is by passing an assembly, or an array of assemblies, each assembly having ActiveRecord classes in them, which are in turn initialised by the ActiveRecordStarter.
A simple demonstration of this, also making use of the in-place configuration is:
ActiveRecordStarter.Initialize(Assembly.GetExecutingAssembly(), ActiveRecordSectionHandler.Instance);
This will make ActiveRecord load the currently executing assembly and try to register all the ActiveRecord classes that it finds.
Building on this, we can have a look at a mechanism that loads all assemblies in the folder that your currently executing application is running. Although there is a certain amount of overhead doing all this processing, it does have the positive side of not having to worry about constantly revisiting your ActiveRecord initialisation code. This is especially true if you've got classes in different assemblies.
To begin with, we need to find all the assemblies in the current folder (presumably all these are all part of your running application), that have a reference to ActiveRecord.
private static Assembly[] GetAssembliesToInitialise()
{
var assembliesToInitialise = new Dictionary<string, Assembly>();
foreach (string dllPath in GetAssemblyPaths())
{
Assembly assembly;
try
{
assembly = Assembly.LoadFile(dllPath);
}
catch //Win32/COM+ DLL's need to be skipped, so catch the exception and continue
{
continue;
}
if (IsAssemblyDependentOnActiveRecord(assembly))
{
string fileName = Path.GetFileName(dllPath);
if (!assembliesToInitialise.ContainsKey(fileName))
assembliesToInitialise.Add(fileName, assembly);
}
}
return assembliesToInitialise.Values.ToArray();
}
private static string[] GetAssemblyPaths()
{
var u = new Uri(Assembly.GetExecutingAssembly().CodeBase);
string directory = Directory.GetCurrentDirectory(); //Use this as the default...
if (u.IsAbsoluteUri)
directory = Path.GetDirectoryName( u.AbsolutePath);
return Directory.GetFiles(directory, "*.DLL", SearchOption.AllDirectories);
}
The code above only searches for files with the .DLL extension - you may want to alter it to search for EXEs as well, or just to search the current directory.
Next we need to determine if the file is an actual assembly and not a Win32/COM+ library. The assembly gets loaded first in the code above (which determines if it's an assembly) and then it is processed to determine if it is dependent on Castle ActiveRecord.
private static bool IsAssemblyDependentOnActiveRecord (Assembly assembly)
{
//Just a small optimisation to skip system files that may be set to copy local
if (assembly.GetName().ToString().ToUpper().StartsWith("SYSTEM."))
return (false);
//Now do the work for other files
AssemblyName[] dependancies = assembly.GetReferencedAssemblies();
foreach (var dependancy in dependancies)
{
if (dependancy.ToString().ToUpper().StartsWith("CASTLE.ACTIVERECORD"))
return (true);
}
return (false);
}
This isn't a sure-fire way of checking that your assembly actually has ActiveRecord class, but it is a good starting point and minimizes some of the work that the ActiveRecordStarter needs to do.
If the assembly passes all these tests, it is added to an array of assemblies, which is the passed to the ActiveRecordStarter.Initialize method which accepts an array of assemblies. The final routine for putting this all together looks as follows:
public static void Initiliase ()
{
//Don't intialise this again if already is initialised
if (ActiveRecordStarter.IsInitialized)
return;
//Get the assemblies that need to be initialised
Assembly[] assembliesToInitilise = GetAssembliesToInitialise();
if (assembliesToInitilise.Length == 0)
return;
//Use the ActiveRecordStarter to initialise the assemblies
ActiveRecordStarter.Initialize(assembliesToInitilise, ActiveRecordSectionHandler.Instance);
}
You can simply call it like this to test it, and in your application, it's a one-liner:
static void Main()
{
ActiveRecordInitialiser.Initiliase();
Console.WriteLine("Is ActiveRecord initialised :" + ActiveRecordStarter.IsInitialized.ToString());
}
If you want to make use of the HttpModule, you need it to your web config file as follows:
<httpModules>
<add name="Darkside.ActiveRecordInitialiser" type="Darkside.ActiveRecord.Configurator.ActiveRecordInitialiserHttpModule, Darkside.ActiveRecord.Configurator"/>
</httpModules>
The module