Я использую рефлексию для выполнения этой (и других) задач.Позвольте мне показать, как это работает.
Первое, что нужно сделать, - это определить интерфейс, который позволит нам идентифицировать классы, выполняющие задачи инициализации:
public interface IConfigurationTask
{
void Configure();
}
Затем создайте один или несколько классов.которые реализуют этот интерфейс.Эти классы будут распределены по всем вашим проектам, и это еще один способ сказать, что вы можете поместить их «туда, где они должны».
public class RepositoryInitializer : IConfigurationTask
{
public void Configure()
{
// code that does relevant initialization goes here
}
}
Последняя часть головоломки - найти классы, которые реализуютИнтерфейс IConfigurationTask, создайте их экземпляр и выполните метод Configure.Это цель ConfigurationTaskRunner:
public static class ConfigurationTaskRunner
{
public static void Execute( params string[] assemblyNames )
{
var assemblies = assemblyNames.Select( Assembly.Load ).Distinct().ToList();
Execute( assemblies );
}
public static void Execute( IEnumerable<Assembly> assemblies )
{
var tasks = new List<IConfigurationTask>();
assemblies.ForEach( a => tasks.AddRange( a.CreateInstances<IConfigurationTask>() ) );
tasks.ForEach( t => t.Configure() );
}
}
Код, показанный здесь, использует пользовательское расширение для перебора всех элементов в списке и выполнения действия для каждого элемента (метод ForEach).Я также использую библиотеку отражений , чтобы сделать задачу нахождения и создания экземпляров одной строкой (метод CreateInstances), но вы можете добиться того же, используя простое отражение (как показано в коде ниже)).
public static IList<T> CreateInstances<T>( this Assembly assembly )
{
var query = from type in assembly.GetTypes().Where( t => typeof(T).IsAssignableFrom( t ) && typeof(T) != t )
where type.IsClass && ! type.IsAbstract && type.GetConstructor( Type.EmptyTypes ) != null
select (T) Activator.CreateInstance( type );
return query.ToList();
}
Последняя часть головоломки - запуск выполнения ConfigurationTaskRunner.Например, в веб-приложении это указывало бы на Application_Start в Global.asax:
// pass in the names of the assemblies we want to scan, hardcoded here as an example
ConfigurationTaskRunner.Execute( "Foo.dll", "Foo.Domain.dll" );
Я также нашел его полезным с производным IPrioritizedConfigurationTask (который добавляет свойство Priority), чтобы разрешить правильное упорядочениезадачи, прежде чем выполнять их.Это не показано в примере кода выше, но добавить его довольно просто.
Надеюсь, это поможет!