С# как "зарегистрировать" "плагины" класса в класс обслуживания?

Я пытаюсь реализовать что-то вроде идеи, которую я пытаюсь показать со следующей диаграммой (конец вопроса).

Все кодируется из классов abstract class Base до DoSomething.

Мой "Сервис" должен предоставить потребительским "действиям" типа "DoSomethings", которые служба "зарегистрировала", на этом этапе я вижу себя как повторение (копирование/вставку) следующей логики на класс обслуживания:

public async Task<Obj1<XXXX>> DoSomething1(....params....)
        {
            var action = new DoSomething1(contructParams);
            return await action.Go(....params....);
        }

Я хотел бы знать , если в С# есть все, чтобы "зарегистрировать" все "DoSomething", которые я хочу по-другому? Что-то более динамичное и менее "копировать/вставлять" и на в то же время предоставляете мне "intellisense" в моем классе потребителей? Somekind "вводит" список принятых "DoSomething" для этой службы.

Update # 1 Прочитав выражение, что PanagiotisKanavos сказал о MEF и проверил другие варианты IoC, я не смог найти именно то, что я ищу.

Моя цель состоит в том, чтобы мой класс Service1 (и все подобные) вел себя как DynamicObject, но где принятые методы определены на собственном конструкторе (где я точно указываю, какой DoSomethingX я предлагаю как вызов метода.

Пример: У меня есть несколько действий (DoSomethingX) как "BuyCar", "SellCar", "ChangeOil", "StartEngine" и т.д.... Теперь я хочу создать сервис "CarService", который должен предлагать только действия "StartEngine" и "SellCar", в то время как у меня могут быть другие "Сервисы" с другой комбинацией "действий". Я хочу определить эту логику внутри конструктора каждой службы. Тогда, в классе потребителя, я просто хочу сделать что-то вроде:

var myCarService = new CarService(...paramsX...);
var res1 = myCarService.StartEngine(...paramsY...);
var res2 = myCarService.SellCar(...paramsZ...);

И я хочу предложить intellisense, когда я использую "CarService"....

В заключение: Цель состоит в том, как "регистрировать" в каждой службе, какие методы предоставляются им, давая список "DoSomethingX" и автоматически предлагая их как "метод"... Надеюсь, я смог объяснить свою цель / пожелание.

Другими словами: Я просто хочу сказать, что мой класс Service1 "предлагает" действия DoSomething1, DoSomething2 и DoSomething3, но с минимальными строками, насколько это возможно. Каким-то образом понятие использования атрибутов класса, где я мог бы сделать что-то похожее на это:

// THEORETICAL CODE
[RegisterAction(typeOf(DoSomething1))]
[RegisterAction(typeOf(DoSomething2))]
[RegisterAction(typeOf(DoSomething3))]
public class Service1{
    // NO NEED OF EXTRA LINES....
}

введите описание изображения здесь

Ответ 1

Для меня MEF/MAF - это то, что вы могли бы сделать последним в такой проблеме. Первый шаг - выработать свой дизайн. Я бы сделал следующее:

  • Внедрите шаблон дизайна декоратора (или подобный структурный шаблон по вашему выбору). Я выбираю декоратор, так как это похоже на то, что вы делаете, добавляя определенные классы с общей функциональностью, которые не определены в этих кланах (например, композиция кажется предпочтительной в вашем примере, а не в наследовании). См. Здесь http://www.dofactory.com/net/decorator-design-pattern

  • Подтвердите шаг 1 POC для разработки, если он будет делать то, что вы хотите, если он будет добавлен как отдельная dll (то есть, создав другой CSProj, испеченный во время сборки).

  • Оцените, подходит ли MEF или MAF для вас (в зависимости от того, какой вес вы хотите поместить). Сравните их с другими методами, такими как микросервисы (которые философски изменили бы ваш нынешний подход).

  • Внедрите свой выбор горячей замены (MEF, вероятно, наиболее логичен на основе информации, которую вы предоставили).

Ответ 2

Вы можете использовать Reflection. В классе Service1 определите список типов BaseAction, которые вы хотите предоставить:

List<Type> providedActions = new List<Type>();
providedActions.Add(typeof(DoSomething1));
providedActions.Add(typeof(DoSomething2));

Затем вы можете написать один метод DoSomething, который выбирает правильный BaseAction во время выполнения:

public async Task<Obj1<XXXX>> DoSomething(string actionName, ....params....)
{
    Type t = providedActions.Find(x => x.Name == actionName);

    if (t != null)
    {
        var action = (BaseAction)Activator.CreateInstance(t);

        return await action.Go(....params....);
    }
    else
        return null;
} 

Недостаток заключается в том, что Клиент не знает действий, предоставляемых службой, если вы не реализуете ad-hoc-метод, например:

public List<string> ProvidedActions()
{
    List<string> lst = new List<string>();
    foreach(Type t in providedActions)
        lst.Add(t.Name);
    return lst;
}

Ответ 3

Может быть RealProxy может вам помочь? Если вы создаете интерфейс ICarService, который наследует IAction1 и IAction2, вы можете создать прокси-объект, который будет:

  • Найти все интерфейсы ICarService наследует.
  • Находит реализации этих интерфейсов (используя действия factory или отражение).
  • Создает список действий для службы.
  • В Invoke метод будет делегировать вызов одному из действий.

Таким образом, у вас будет интеллект, как вы хотите, и действия будут создавать блоки для служб. Какой-то вид наследования с несколькими наследствами:)

Ответ 4

В этот момент я действительно испытываю искушение сделать следующее:

  • Создайте свой собственный атрибут класса RegisterAction (точно так же, как я написал на моем "теоретическом" примере)
  • Расширьте Процесс сборки Visual Studio
  • Затем на моем public class LazyProgrammerSolutionTask: Microsoft.Build.Utilities.Task попробуйте найти классы обслуживания и определить атрибуты RegisterAction.
  • Затем для каждого из них я буду вводить с использованием отражения свой собственный метод (тот, который я всегда копирую вставку)... и, конечно же, получить "подпись" из соответствующего целевого класса "действие".
  • В конце концов, скомпилируйте все снова.
  • Тогда мой "следующий проект", который будет потреблять этот проект (библиотека), будет иметь интеллект, который я ищу....
  • Одна вещь, что я действительно не уверен, это то, как "debug" будет работать над этим....

Так как это еще и теоретически (BUT ВОЗМОЖНО) решение, у меня пока нет исходного кода.

Между тем, я оставлю этот вопрос открытым для других возможных подходов.