Зерно доступа без интерфейса

Я планирую свою систему IoT и сравниваю Akka.NET с Orleans. В целом Orleans API более близок к стандартам .NET, но мне интересно, есть ли другой способ связи с Grain? В документации я вижу, что мы определяем интерфейс как

public interface IDevice : IGrainWithGuidKey
{
  Task TurnOn(TurnOnCommand command);
  Task TurnOff(TurnOffCommand command);
}

И использовать это так

IDevice device = GrainFactory.GetGrain<IDevice>(id);

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

device.Execute(new TurnOnCommand()) 

без определения интерфейса для каждого. Возможно ли это в Орлеане?


person dnf    schedule 30.08.2018    source источник


Ответы (1)


Вы можете определить интерфейс, который принимает базовый класс/интерфейс, такой как ICommandProcessor<TCommand>, и реализовать несколько его версий, поэтому у вас может быть IMyDeviceGrain, реализующий ICommandProcessor<TurnOnCommand>, ICommandProcessor<TurnOffCommand>, и так далее.

В этом случае вы можете определить ICommandProcessor<TCommand> как:

public interface ICommandProcessor<TCommand>
{
    Task Process(TCommand command);   
}

Интерфейс зерна может выглядеть так:

public class IDeviceGrain :
  IGrainWithStringKey,
  ICommandProcessor<TurnOnCommand>,
  ICommandProcessor<TurnOffCommand>
{
}

Тогда класс зерна может выглядеть так:

public class DeviceGrain : Grain, IDeviceGrain
{
    Task Process(TurnOnCommand command) { /* turn on */ }
    Task Process(TurnOffCommand command) { /* turn off */ }
}

Я также видел, как люди реализовывали это, имея зернистость с одним вызовом Task Process(object command), который внутренне использовал dynamic для отправки на основе типа command.

Надеюсь, это поможет. Пожалуйста, дайте мне знать, если есть какие-либо моменты, которые вы хотели бы уточнить.

person Reuben Bond    schedule 31.08.2018
comment
Я тоже думал об этом на секунду, но у этого есть некоторый недостаток. Устройств много и у одного одна команда, а у другого 20 — со вторым классом объявление будет большим. Во-вторых, мне придется определить отдельный интерфейс для разных наборов функций — устройство с SetVolumeCommand будет иметь отдельный интерфейс. Со многими устройствами это будет интерфейсный ад. Я, конечно, могу создать одну команду в качестве входных данных и иметь большой оператор if, который направит ее во внутренние обработчики, но писать этот код маршрутизатора — это то, что я бы не стал делать ;( - person dnf; 01.09.2018
comment
В вашем случае @dnf вы можете использовать более общий метод для получения команд. Например, он может принимать CommandId (который может быть строкой) и некоторые аргументы команды (которые могут быть массивом строк или объектов). Затем у вас может быть словарь в вашем зерне для сопоставления CommandId с фактической командой (которая может быть выражена как делегат) - person Reuben Bond; 02.09.2018
comment
После некоторого размышления я решил пойти с созданием прокси-класса с Roslyn, который будет генерировать весь шаблонный код во время сборки с помощью github.com/AArnott/CodeGeneration.Рослин - person dnf; 07.09.2018
comment
Что будет на входе генератора кода? Если у вас есть интерфейс, Orleans может сгенерировать для вас код. Если вы хотите обсудить это еще раз, вы можете связаться со мной на Gitter gitter.im/ReubenBond или на канале Orleans gitter.im/dotnet/orleans - person Reuben Bond; 07.09.2018