С# как "зарегистрировать" "плагины" класса в класс обслуживания?
Я пытаюсь реализовать что-то вроде идеи, которую я пытаюсь показать со следующей диаграммой (конец вопроса).
Все кодируется из классов 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 ВОЗМОЖНО) решение, у меня пока нет исходного кода.
Между тем, я оставлю этот вопрос открытым для других возможных подходов.