Получить экземпляр типа, наследующий от базового класса, реализующий интерфейс, используя StructureMap - PullRequest
2 голосов
/ 27 апреля 2010

Продолжая поиски хорошей реализации плагина, я тестировал функции сканирования сборок StructureMap.

Все плагины будут наследоваться от абстрактного класса PluginBase.Это обеспечит доступ к общим службам приложений, таким как ведение журнала.В зависимости от его функции каждый плагин может затем реализовать дополнительные интерфейсы, например, IStartUpTask.

Я инициализирую свои плагины следующим образом:

            Scan(x => {
            x.AssembliesFromPath(HttpContext.Current.Server.MapPath("~/Plugins"),
                assembly => assembly.GetName().Name.Contains("Extension"));             
            x.AddAllTypesOf<PluginBase>();
        });

Сложность, с которой я тогда сталкиваюсь, заключается вработать против интерфейса (не PluginBase) в коде.Работать с PluginBase достаточно просто:

            var plugins = ObjectFactory.GetAllInstances<PluginBase>();

        foreach (var plugin in plugins)
        {

        }

Но конкретные функции (например, IStartUpTask.RunTask) привязаны к интерфейсу, а не к базовому классу.

Я понимаю, что это может быть не совсемв структурную карту (возможно, это еще вопрос к размышлению).

Спасибо, Бен

Ответы [ 2 ]

2 голосов
/ 27 апреля 2010

Знаете ли вы все конкретные интерфейсы во время регистрации? Если это так, вы можете создать пользовательское соглашение о регистрации, которое регистрирует каждый тип с помощью «семейства» плагинов интерфейса, который он реализует. IRegistrationConvention получает каждый тип, по одному за раз. Вы можете сделать простую проверку, чтобы увидеть, реализует ли текущий тип желаемый интерфейс, и если это так, добавить его.

if (typeof(IStartUpTask).IsAssignableFrom(currentType)){
  For<IStartUpTask>().Add(currentType);
}

Затем в коде вы можете получить плагины для каждого конкретного интерфейса в отдельности:

var startupTasks = ObjectFactory.GetAllInstances<IStartUpTask>();

Преимущество этого подхода состоит в том, что он позволяет вводить перечисляемые плагины пользовательского интерфейса в класс, который в них нуждается, вместо вызова службы определения местоположения.

В качестве альтернативы, если вы не хотите заключать соглашение о регистрации, вы можете просто выполнить фильтрацию во время выполнения, используя удобный OfType метод расширения linq:

var startupTasks = ObjectFactory.GetAllInstances<PluginBase>().OfType<IStartupTask>();
0 голосов
/ 27 апреля 2010

В случае, если это помогает другим, я последовал совету Джошуа и добавил свое собственное соглашение о регистрации:

public class PluginConvention : IRegistrationConvention
{
    public void Process(Type type, Registry registry) {
        if (type.BaseType == null) return;

        if (type.BaseType.Equals(typeof(PSAdmin.Core.Domain.PluginBase))) {
            if (typeof(IStartUpTask).IsAssignableFrom(type)) {
                registry.For<IStartUpTask>()
                    .TheDefault.Is.OfConcreteType(type);
            }
        }
    }
}

Я не смог заставить работать метод .Add, независимо от того, что я пытался, поэтому пришлосьиспользуйте TheDefault.Is.OfConcreteType (type).

Затем в моем загрузчике я сканирую примерно так:

        Scan(x => {
            x.AssembliesFromPath(HttpContext.Current.Server.MapPath("~/Plugins"), 
                assembly => assembly.GetName().Name.Contains("Extension"));
            x.Convention<PluginConvention>();
        });

Затем я могу получить свои типы задач IStartUp, например, так:

        var plugins = ObjectFactory.GetAllInstances<IStartUpTask>();              

        foreach (var plugin in plugins)
        {
            plugin.Configure();
        }

Тем не менее, после ознакомления с некоторыми новыми функциями StructureMap я не уверен, что мне нужно делать что-либо из перечисленного выше.Например, я мог бы просто изменить свою делегатскую функцию Scan на:

        Scan(x => {
            x.AssembliesFromPath(HttpContext.Current.Server.MapPath("~/Plugins"),
                assembly => assembly.GetName().Name.Contains("Extension"));
            x.AddAllTypesOf<PluginBase>();
        });

И использовать конкретные типы моего интерфейса (которые наследуются от PluginBase):

        var tasks = ObjectFactory.Model.GetAllPossible<IStartUpTask>();

        foreach (var task in tasks)
        {
            task.Configure();
        }

Кажется, что оба метода достигаютто же самое.

...