123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810 |
- using com.sun.jmx.mbeanserver;
- using Comal.Classes;
- using Expressive;
- using InABox.Core;
- using InABox.Database;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Serialization;
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Linq;
- using System.Reflection;
- using System.Text;
- using System.Threading.Tasks;
- namespace PRS.Shared.Events;
- public interface IEventData
- {
- IEvent Event { get; }
- IEnumerable<string> ReferencedVariables { get; }
- }
- public class EventData<T, TDataModel> : IEventData
- where T : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- public T Event { get; set; }
- /// <summary>
- /// A list of triggers for this event. If any of the triggers match, the event runs.
- /// </summary>
- public List<IEventTrigger<T, TDataModel>> Triggers { get; set; }
- public List<IEventAction<T>> Actions { get; set; }
- IEvent IEventData.Event => Event;
- public IEnumerable<string> ReferencedVariables =>
- Triggers.SelectMany(x => x.ReferencedVariables)
- .Concat(Actions.SelectMany(x => x.ReferencedVariables))
- .Distinct();
- public EventData(T eventData)
- {
- Event = eventData;
- Triggers = new List<IEventTrigger<T, TDataModel>>();
- Actions = new List<IEventAction<T>>();
- }
- public Notification GenerateNotification(TDataModel model) => Event.GenerateNotification(model);
- }
- public static class EventUtils
- {
- #region Serialisation
- private class WritablePropertiesOnlyResolver : DefaultContractResolver
- {
- protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
- {
- IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
- return props.Where(p => p.Writable).ToList();
- }
- }
- private static JsonSerializerSettings SerializationSettings()
- {
- var settings = Serialization.CreateSerializerSettings();
- settings.TypeNameHandling = TypeNameHandling.Auto;
- settings.ContractResolver = new WritablePropertiesOnlyResolver();
- return settings;
- }
- public static string Serialize(IEventData data)
- {
- return JsonConvert.SerializeObject(data, typeof(IEventData), SerializationSettings());
- }
- public static IEventData Deserialize(string json)
- {
- return JsonConvert.DeserializeObject<IEventData>(json, SerializationSettings())!;
- }
- #endregion
- #region Event Types
- private static Dictionary<Type, List<Type>>? _eventTriggerTypes;
- private static Dictionary<Type, List<Type>>? _eventActionTypes;
- private static List<Type>? _standardEventActionTypes;
- [MemberNotNullWhen(true, nameof(_eventTriggerTypes), nameof(_eventActionTypes), nameof(_standardEventActionTypes))]
- private static bool _loadedTypes { get; set; }
- [MemberNotNull(nameof(_eventTriggerTypes), nameof(_eventActionTypes), nameof(_standardEventActionTypes))]
- private static void LoadTypes()
- {
- if (_loadedTypes) return;
- _loadedTypes = true;
- _eventTriggerTypes = new();
- _eventActionTypes = new();
- _standardEventActionTypes = new();
- foreach(var type in CoreUtils.TypeList(x => true))
- {
- if (type.GetInterfaceDefinition(typeof(IEventTrigger<,>)) is Type eventTriggerInterface)
- {
- if (type.GetConstructor([]) is not null)
- {
- var eventType = eventTriggerInterface.GenericTypeArguments[0];
- eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
- _eventTriggerTypes.GetValueOrAdd(eventType).Add(type);
- }
- }
- else if(type.GetInterfaceDefinition(typeof(IStandardEventAction<>)) is Type standardEventAction)
- {
- if (type.GetConstructor([]) is not null)
- {
- _standardEventActionTypes.Add(type);
- }
- }
- else if (type.GetInterfaceDefinition(typeof(IEventAction<>)) is Type eventActionInterface)
- {
- if (type.GetConstructor([]) is not null)
- {
- var eventType = eventActionInterface.GenericTypeArguments[0];
- eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
- _eventActionTypes.GetValueOrAdd(eventType).Add(type);
- }
- }
- }
- }
- public static IEnumerable<Type> GetEventTriggerTypes(Type eventType)
- {
- LoadTypes();
- eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
- return _eventTriggerTypes.GetValueOrDefault(eventType) ?? Enumerable.Empty<Type>();
- }
- public static IEnumerable<Type> GetEventActionTypes(Type eventType)
- {
- LoadTypes();
- eventType = eventType.IsGenericType ? eventType.GetGenericTypeDefinition() : eventType;
- return _eventActionTypes.GetValueOrDefault(eventType) ?? Enumerable.Empty<Type>();
- }
- public static IEnumerable<Type> GetStandardEventActionTypes()
- {
- LoadTypes();
- return _standardEventActionTypes;
- }
- #endregion
- #region Execution
- private static bool Check<T, TDataModel>(EventData<T, TDataModel> ev, TDataModel dataModel)
- where T : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- return ev.Triggers.Count == 0 || ev.Triggers.Any(x => x.Check(dataModel));
- }
- public static void Run<T, TDataModel>(IStore store, Event ev, EventData<T, TDataModel> evData, TDataModel dataModel)
- where T : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- if (!Check(evData, dataModel)) return;
- foreach(var action in evData.Actions)
- {
- IEvent.RunAction(evData.Event, dataModel, action);
- }
- NotifySubscribers(store, ev, evData, dataModel);
- }
- public static void SendNotifications(IStore store, IEnumerable<(EventSubscriberType type, Notification notification)> notifications)
- {
- var toSave = new List<Notification>();
- foreach(var (type, notification) in notifications)
- {
- if(type == EventSubscriberType.Notification)
- {
- toSave.Add(notification);
- }
- else if(type == EventSubscriberType.Email)
- {
- var mailer = DbFactory.Mailer;
- if(mailer is not null)
- {
- if (mailer.Connect())
- {
- var msg = mailer.CreateMessage();
- // msg.From = DbFactory.EmailAddress;
- msg.To = [notification.Employee.Email];
- msg.Subject = notification.Title;
- msg.Body = notification.Description;
- var bOK = mailer.SendMessage(msg);
- }
- }
- }
- }
- store.Provider.Save(toSave);
- }
- private static void NotifySubscribers<T, TDataModel>(IStore store, Event ev, EventData<T, TDataModel> evData, TDataModel dataModel)
- where T : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- if (!ev.NotificationsEnabled) return;
- string? description;
- if (ev.NotificationExpression.IsNullOrWhiteSpace())
- {
- description = null;
- }
- else
- {
- var descriptionExpr = new CoreExpression(ev.NotificationExpression, typeof(string));
- if(!descriptionExpr.TryEvaluate<string>(dataModel).Get(out description, out var error))
- {
- CoreUtils.LogException(store.UserID, error, extra: "Error notifying subscribers", store.Logger);
- return;
- }
- }
- var subscribers = store.Provider.Query(
- new Filter<EventSubscriber>(x => x.Event.ID).IsEqualTo(ev.ID),
- Columns.None<EventSubscriber>().Add(x => x.Employee.ID).Add(x => x.Employee.Email).Add(x => x.SubscriberType))
- .ToArray<EventSubscriber>();
- SendNotifications(store, subscribers.Select(x =>
- {
- var notification = evData.GenerateNotification(dataModel);
- notification.Employee.CopyFrom(x.Employee);
- if (description is not null)
- {
- notification.Description = description;
- }
- return (x.SubscriberType, notification);
- }));
- }
- #endregion
- #region Event Cache
- private static bool _loadedCache = false;
- private static Dictionary<EventType, Dictionary<Type, List<(Event Event, IEventData EventData)>>> _entityEvents = new();
- private static Dictionary<Guid, (EventType, Type)> _entityEventMap = new();
- private static Dictionary<EventType, List<(Event Event, IEventData EventData)>> _genericEvents = new();
- private static Dictionary<Guid, EventType> _genericEventMap = new();
- public static Type GetEventType(EventType type)
- {
- switch (type)
- {
- case EventType.AfterSave:
- return typeof(SaveEvent<>);
- case EventType.Scheduled:
- return typeof(ScheduledEvent);
- default:
- throw new Exception($"Invalid event type {type}");
- }
- }
- public static void ReloadCache(IProvider provider)
- {
- _loadedCache = true;
- _entityEvents.Clear();
- _entityEventMap.Clear();
- _genericEvents.Clear();
- _genericEventMap.Clear();
- var events = provider.Query(
- new Filter<Event>(x => x.Enabled).IsEqualTo(true),
- Columns.None<Event>().Add(x => x.ID).Add(x => x.Code).Add(x => x.EventType).Add(x => x.Data).Add(x => x.NotificationExpression).Add(x => x.NotificationsEnabled))
- .ToObjects<Event>();
- foreach(var ev in events)
- {
- AddEvent(provider, ev);
- }
- }
- private static void AddGenericEvent(Event ev)
- {
- if(ev.Data is not null && ev.Data.Length != 0)
- {
- var data = Deserialize(ev.Data);
- var list = _genericEvents.GetValueOrAdd(ev.EventType);
- list.RemoveAll(x => x.Event.ID == ev.ID);
-
- list.Add((ev, data));
- _genericEventMap[ev.ID] = ev.EventType;
- }
- }
- private static void RemoveGenericEvent(Event ev)
- {
- if(_genericEventMap.Remove(ev.ID, out var evType))
- {
- if(_genericEvents.TryGetValue(evType, out var eventTypeEvents))
- {
- eventTypeEvents.RemoveAll(x => x.Event.ID == ev.ID);
- if(eventTypeEvents.Count == 0)
- {
- _genericEvents.Remove(evType);
- }
- }
- }
- }
- private static void AddEntityEvent(Event ev)
- {
- if(ev.Data is not null && ev.Data.Length != 0)
- {
- var data = Deserialize(ev.Data);
- var entityType = data.Event.GetType().GenericTypeArguments[0];
- var list = _entityEvents.GetValueOrAdd(ev.EventType).GetValueOrAdd(entityType);
- list.RemoveAll(x => x.Event.ID == ev.ID);
-
- list.Add((ev, data));
- _entityEventMap[ev.ID] = (ev.EventType, entityType);
- }
- }
- private static void RemoveEntityEvent(Event ev)
- {
- if(_entityEventMap.TryGetValue(ev.ID, out var item))
- {
- if(_entityEvents.TryGetValue(item.Item1, out var eventTypeEvents))
- {
- if(eventTypeEvents.TryGetValue(item.Item2, out var entityEvents))
- {
- entityEvents.RemoveAll(x => x.Event.ID == ev.ID);
- if(entityEvents.Count == 0)
- {
- eventTypeEvents.Remove(item.Item2);
- }
- }
- if(eventTypeEvents.Count == 0)
- {
- _entityEvents.Remove(item.Item1);
- }
- }
- _entityEventMap.Remove(ev.ID);
- }
- }
- public static void AddEvent(IProvider provider, Event ev)
- {
- if (!_loadedCache) ReloadCache(provider);
- var type = GetEventType(ev.EventType);
- if (type.HasInterface(typeof(IEntityEvent<>)))
- {
- AddEntityEvent(ev);
- }
- else
- {
- AddGenericEvent(ev);
- }
- }
- public static void RemoveEvent(IProvider provider, Event ev)
- {
- if (!_loadedCache) ReloadCache(provider);
- var type = GetEventType(ev.EventType);
- if (type.HasInterface(typeof(IEntityEvent<>)))
- {
- RemoveEntityEvent(ev);
- }
- else
- {
- RemoveGenericEvent(ev);
- }
- }
- #endregion
- #region Public Event Running Interface
- public static void AfterSave<T>(IStore store, T entity)
- where T : Entity, new()
- {
- if (!_loadedCache) ReloadCache(store.Provider);
- var events = _entityEvents.GetValueOrDefault(EventType.AfterSave)
- ?.GetValueOrDefault(typeof(T));
- if (events is null) return;
- foreach(var ev in events)
- {
- var eventData = (ev.EventData as EventData<SaveEvent<T>, SaveEventDataModel<T>>)!;
- var model = new SaveEventDataModel<T>(entity, store);
- eventData.Event.Init(store, ev.Event, eventData, model);
- Run(store, ev.Event, eventData, model);
- }
- }
- private static bool CheckEventSchedule(ScheduledEvent ev)
- {
- var now = DateTime.Now;
- switch (ev.Properties.Period)
- {
- case SchedulePeriod.Minute:
- case SchedulePeriod.Hour:
- case SchedulePeriod.Day:
- var dayOfWeekSettings = ev.Properties.DayOfWeekSettings.FirstOrDefault(x => x.DayOfWeek == now.DayOfWeek);
- if(dayOfWeekSettings is null || !dayOfWeekSettings.Enabled)
- {
- return false;
- }
- var start = now - now.TimeOfDay + dayOfWeekSettings.StartTime;
- var end = now - now.TimeOfDay + dayOfWeekSettings.EndTime;
- if(ev.Properties.Period == SchedulePeriod.Day)
- {
- if(ev.Properties.LastExecution < start && now >= start && (now.Date - ev.Properties.LastExecution.Date).TotalDays >= ev.Properties.Frequency)
- {
- ev.Properties.LastExecution = start;
- return true;
- }
- else
- {
- return false;
- }
- }
- else if(ev.Properties.Period == SchedulePeriod.Hour)
- {
- if(start <= now && now <= end)
- {
- var nowIntervals = Math.Floor((now - start).TotalHours / ev.Properties.Frequency);
- var lastIntervals = Math.Floor((ev.Properties.LastExecution - start).TotalHours / ev.Properties.Frequency);
- if(nowIntervals > lastIntervals)
- {
- ev.Properties.LastExecution = start.AddHours(nowIntervals * ev.Properties.Frequency);
- }
- return true;
- }
- else
- {
- return false;
- }
- }
- else if(ev.Properties.Period == SchedulePeriod.Minute)
- {
- if (start <= now && now <= end)
- {
- var nowIntervals = Math.Floor((now - start).TotalMinutes / ev.Properties.Frequency);
- var lastIntervals = Math.Floor((ev.Properties.LastExecution - start).TotalMinutes / ev.Properties.Frequency);
- if (nowIntervals > lastIntervals)
- {
- ev.Properties.LastExecution = start.AddMinutes(nowIntervals * ev.Properties.Frequency);
- }
- return true;
- }
- else
- {
- return false;
- }
- }
- else
- {
- throw new Exception("Invalid state");
- }
- case SchedulePeriod.Week:
- if (now >= ev.Properties.NextSchedule)
- {
- while (now >= ev.Properties.NextSchedule)
- {
- ev.Properties.NextSchedule += TimeSpan.FromDays(7 * ev.Properties.Frequency);
- }
- return true;
- }
- else
- {
- return false;
- }
- case SchedulePeriod.Month:
- if(now >= ev.Properties.NextSchedule)
- {
- while(now >= ev.Properties.NextSchedule)
- {
- ev.Properties.NextSchedule = ev.Properties.NextSchedule.AddMonths(ev.Properties.Frequency);
- }
- return true;
- }
- else
- {
- return false;
- }
- case SchedulePeriod.Year:
- if(now >= ev.Properties.NextSchedule)
- {
- while(now >= ev.Properties.NextSchedule)
- {
- ev.Properties.NextSchedule = ev.Properties.NextSchedule.AddYears(ev.Properties.Frequency);
- }
- return true;
- }
- else
- {
- return false;
- }
- default:
- throw new Exception("Invalid state");
- }
- }
- public static void CheckScheduledEvents()
- {
- var store = DbFactory.FindStore<Event>(Guid.Empty, "", Platform.DatabaseEngine, CoreUtils.GetVersion(), Logger.New());
- if (!_loadedCache) ReloadCache(store.Provider);
- var events = _genericEvents.GetValueOrDefault(EventType.Scheduled);
- if (events is null) return;
- foreach(var ev in events)
- {
- var eventData = (ev.EventData as EventData<ScheduledEvent, ScheduledEventDataModel>)!;
- if (CheckEventSchedule(eventData.Event))
- {
- var model = new ScheduledEventDataModel(ev.Event);
- Run(store, ev.Event, eventData, model);
- ev.Event.Data = Serialize(eventData);
- store.Provider.Save(ev.Event);
- }
- }
- }
- #endregion
- }
- #region DataModel Definition
- public interface IEventVariable
- {
- string Name { get; set; }
- Type Type { get; set; }
- }
- public class StandardEventVariable : IEventVariable
- {
- public string Name { get; set; }
- public Type Type { get; set; }
- public StandardEventVariable(string name, Type type)
- {
- Name = name;
- Type = type;
- }
- }
- public class ListEventVariable : IEventVariable
- {
- public string Name { get; set; }
- public Type Type { get; set; }
- public ListEventVariable(string name, Type type)
- {
- Name = name;
- Type = type;
- }
- }
- public interface IEventDataModelDefinition
- {
- IEnumerable<IEventVariable> GetVariables();
- IEventVariable? GetVariable(string name);
- }
- #endregion
- #region DataModel
- public interface IEventDataModel : IVariableProvider
- {
- bool TryGetVariable(string name, out object? value);
- bool IVariableProvider.TryGetValue(string variableName, out object? value)
- {
- return TryGetVariable(variableName, out value);
- }
- T RootModel<T>()
- where T : IEventDataModel
- {
- if(this is IChildEventDataModel child)
- {
- return child.Parent.RootModel<T>();
- }
- else if(this is T root)
- {
- return root;
- }
- else
- {
- throw new Exception($"Root model of wrong type; expected {typeof(T).FullName}; got {GetType().FullName}");
- }
- }
- }
- public interface ITypedEventDataModel : IEventDataModel
- {
- public Type EntityType { get; }
- }
- public interface IChildEventDataModel : IEventDataModel
- {
- IEventDataModel Parent { get; }
- }
- public class ChildEventDataModel : IChildEventDataModel
- {
- public IEventDataModel Parent { get; set; }
- public Dictionary<string, object?> Values { get; set; } = new Dictionary<string, object?>();
- public ChildEventDataModel(IEventDataModel parent)
- {
- Parent = parent;
- }
- public bool TryGetVariable(string name, out object? value)
- {
- return Values.TryGetValue(name, out value)
- || Parent.TryGetVariable(name, out value);
- }
- public void Init(IEventData data)
- {
- }
- }
- #endregion
- public interface IEvent
- {
- IEventDataModelDefinition DataModelDefinition();
- public static void RunAction(IEvent ev, IEventDataModel model, IEventAction action)
- {
- var dataModelDef = ev.DataModelDefinition();
- var values = new List<(string name, object? value)[]>() { Array.Empty<(string, object?)>() };
- foreach(var variable in action.ReferencedVariables)
- {
- var varDef = dataModelDef.GetVariable(variable);
- if(varDef is ListEventVariable)
- {
- if (model.TryGetVariable(varDef.Name, out var list) && list is IEnumerable enumerable)
- {
- var oldValues = values;
- values = new List<(string name, object? value)[]>();
- foreach(var item in enumerable)
- {
- foreach(var valueList in oldValues)
- {
- values.Add(valueList.Concatenate(new (string name, object? value)[]
- {
- (varDef.Name, item)
- }));
- }
- }
- }
- else
- {
- values.Clear();
- break;
- }
- }
- }
- if(values.Count > 0 && (values.Count > 1 || values[0].Length > 0))
- {
- var subModel = new ChildEventDataModel(model);
- foreach(var valueSet in values)
- {
- subModel.Values.Clear();
- foreach(var (name, value) in valueSet)
- {
- subModel.Values[name] = value;
- }
- action.Execute(subModel);
- }
- }
- else
- {
- action.Execute(model);
- }
- }
- }
- /// <summary>
- /// Indicates that this event type has properties that can be edited; <typeparamref name="TProperties"/> is used to edit these properties
- /// with a dynamic editor.
- /// </summary>
- public interface IPropertiesEvent<TProperties>
- where TProperties : BaseObject, new()
- {
- /// <summary>
- /// Create the properties object for editing.
- /// </summary>
- TProperties GetProperties();
- /// <summary>
- /// Given an edited properties object, save them again.
- /// </summary>
- void SetProperties(TProperties properties);
- }
- /// <summary>
- /// Indicates that this event is generic for type <typeparamref name="T"/>; if this interface is implemented,
- /// the main type <b>must</b> be generic, with only one generic type argument.
- /// </summary>
- public interface IEntityEvent<T>
- where T : Entity
- {
- }
- public interface IEvent<TDataModel> : IEvent
- {
- Notification GenerateNotification(TDataModel model);
- }
- public interface IEventTrigger
- {
- IEnumerable<string> ReferencedVariables { get; }
- string Description { get; }
- }
- public interface IEventTrigger<TEvent, TDataModel> : IEventTrigger
- where TEvent : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- bool Check(TDataModel dataModel);
- }
- public interface IEventTriggerParent<TEvent, TDataModel> : IEventTrigger<TEvent, TDataModel>
- where TEvent : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- public List<IEventTrigger<TEvent, TDataModel>> ChildTriggers { get; }
- }
- public class AndTrigger<TEvent, TDataModel> : IEventTrigger<TEvent, TDataModel>, IEventTriggerParent<TEvent, TDataModel>
- where TEvent : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- public List<IEventTrigger<TEvent, TDataModel>> ChildTriggers { get; set; } = new();
- public IEnumerable<string> ReferencedVariables => ChildTriggers.SelectMany(x => x.ReferencedVariables);
- public string Description => "And";
- public bool Check(TDataModel dataModel)
- {
- return ChildTriggers.All(x => x.Check(dataModel));
- }
- }
- public class OrTrigger<TEvent, TDataModel> : IEventTrigger<TEvent, TDataModel>, IEventTriggerParent<TEvent, TDataModel>
- where TEvent : IEvent<TDataModel>
- where TDataModel : IEventDataModel
- {
- public List<IEventTrigger<TEvent, TDataModel>> ChildTriggers { get; set; } = new();
- public IEnumerable<string> ReferencedVariables => ChildTriggers.SelectMany(x => x.ReferencedVariables);
- public string Description => "Or";
- public bool Check(TDataModel dataModel)
- {
- return ChildTriggers.Any(x => x.Check(dataModel));
- }
- }
- public interface IEventAction
- {
- IEnumerable<string> ReferencedVariables { get; }
- object? Execute(IEventDataModel dataModel);
- string Description { get; }
- }
- public interface IEventAction<TEvent> : IEventAction
- where TEvent : IEvent
- {
- }
- /// <summary>
- /// Marks this event action as being applicable to any event type.
- /// </summary>
- public interface IStandardEventAction<TEvent> : IEventAction<TEvent>
- where TEvent : IEvent
- {
- }
|