Server user-defined actions
In DWKit, you can write special actions that will be executed upon changing or requesting data via DWKit ORM. They come in three types.
- Trigger - action of
the
TriggerResult TriggerAction(EntityModel model, List<dynamic> entities, TriggerExecutionContext context, dynamic options)
type, called before or after changing (insert, update, delete) or after select. You can modify the set of saved or returned data, enable calling of business logic, or cancel the entire operation in the triggers. - Filter - action of the
Filter FilterAction(EntityModel model, List<dynamic> entities, dynamic options)
type, returns a filter that is applied to the query. - Action - action of the
object Action(dynamic request)
type - a server method of business logic.
You can create Server user-defined Actions both in the admin application and in the code of your application, creating classes
implementing the IServerActionsProvider
interface.
Editing server actions in the admin panel
Server actions can be edited in the Code Actions section in the admin panel. Here's how server action editing interface looks like in the admin panel:
Control elements in this interface have the following functions:
- Link to the list of all server actions registered in the system.
- Opened server action name.
- Compile - button to test action compilation. If there are any errors in the code, they will be displayed in the bottom of this form.
- Save - Save button.
- Cancel - Button to cancel all changes.
- Name - server action name is to be unique in the system. DWKit call server action by this name.
- Type - server action type: Trigger, Filter or Action.
- IsAsync - if this checkbox is checked, action will be called asynchronously.
- Usings - list of usings, required for code compilation. Must be separated by the ';' symbol.
- Comment - comment to action, non-required.
- Server Action method signature. Depends on Type and Is Async. Is included for info purposes.
- Server action code in C#.
IServerActionsProvider implementation
Look at the example of IServerActionsProvider
implementation. You can copy and paste it into your application.
public class ActionsProvider : IServerActionsProvider
{
private Dictionary<string, Func<EntityModel, List<dynamic>, dynamic, Filter>> _filters
= new Dictionary<string, Func<EntityModel, List<dynamic>, dynamic, Filter>>();
private Dictionary<string, Func<EntityModel, List<dynamic>, dynamic, Task<Filter>>> _filtersAsync
= new Dictionary<string, Func<EntityModel, List<dynamic>, dynamic, Task<Filter>>>();
private Dictionary<string, Func<EntityModel, List<dynamic>, TriggerExecutionContext, dynamic, TriggerResult>> _triggers
= new Dictionary<string, Func<EntityModel, List<dynamic>, TriggerExecutionContext, dynamic, TriggerResult>>();
private Dictionary<string, Func<EntityModel, List<dynamic>, TriggerExecutionContext, dynamic, Task<TriggerResult>>> _triggersAsync
= new Dictionary<string, Func<EntityModel, List<dynamic>, TriggerExecutionContext, dynamic, Task<TriggerResult>>>();
private Dictionary<string, Func<dynamic, dynamic>> _actions
= new Dictionary<string, Func<dynamic, dynamic>>();
private Dictionary<string, Func<dynamic, Task<dynamic>>> _actionsAsync
= new Dictionary<string, Func<dynamic, Task<dynamic>>>();
#region IServerActionsProvider implementation
public List<string> GetFilterNames()
{
return _filters.Keys.Concat(_filtersAsync.Keys).ToList();
}
public bool IsFilterAsync(string name)
{
return _filtersAsync.ContainsKey(name);
}
public bool ContainsFilter(string name)
{
return _filtersAsync.ContainsKey(name) || _filters.ContainsKey(name);
}
public Filter GetFilter(string name, EntityModel model, List<dynamic> entities, dynamic options)
{
if (_filters.ContainsKey(name))
return _filters[name](model, entities, options);
throw new NotImplementedException();
}
public Task<Filter> GetFilterAsync(string name, EntityModel model, List<dynamic> entities, dynamic options)
{
if (_filtersAsync.ContainsKey(name))
return _filtersAsync[name](model, entities, options);
throw new NotImplementedException();
}
public List<string> GetTriggerNames()
{
return _triggers.Keys.Concat(_triggersAsync.Keys).ToList();
}
public bool IsTriggerAsync(string name)
{
return _triggersAsync.ContainsKey(name);
}
public bool ContainsTrigger(string name)
{
return _triggersAsync.ContainsKey(name) || _triggers.ContainsKey(name);
}
public TriggerResult ExecuteTrigger(string name, EntityModel model, List<dynamic> entities, TriggerExecutionContext context, dynamic options)
{
if (_triggers.ContainsKey(name))
return _triggers[name](model, entities, context, options);
throw new System.NotImplementedException();
}
public Task<TriggerResult> ExecuteTriggerAsync(string name, EntityModel model, List<dynamic> entities, TriggerExecutionContext context, dynamic options)
{
if (_triggersAsync.ContainsKey(name))
return _triggersAsync[name](model, entities, context, options);
throw new System.NotImplementedException();
}
public List<string> GetActionNames()
{
return _actions.Keys.Concat(_actionsAsync.Keys).ToList();
}
public bool IsActionAsync(string name)
{
return _actionsAsync.ContainsKey(name);
}
public bool ContainsAction(string name)
{
return _actions.ContainsKey(name) || _actionsAsync.ContainsKey(name);
}
public object ExecuteAction(string name, dynamic request)
{
if (_actions.ContainsKey(name))
return _actions[name](request);
throw new NotImplementedException();
}
public async Task<object> ExecuteActionAsync(string name, dynamic request)
{
if (_actionsAsync.ContainsKey(name))
return _actionsAsync[name](request);
throw new NotImplementedException();
}
#endregion
public ActionsProvider()
{
_filtersAsync.Add("CustomAsyncFilter", CustomAsyncFilter);
_filters.Add("CustomFilter", CustomFilter);
_triggersAsync.Add("CustomAsyncTrigger", CustomAsyncTrigger);
_triggers.Add("CustomTrigger", CustomTrigger);
_actionsAsync.Add("CustomAsyncAction", CustomAsyncAction);
_actions.Add("CustomAction", CustomAction);
}
#region Filters
private async Task<Filter> CustomAsyncFilter(EntityModel model, List<object> entities, dynamic options)
{
return Filter.Empty;
}
private Filter CustomFilter(EntityModel model, List<object> entities, dynamic options)
{
return Filter.Empty;
}
#endregion
#region Triggers
private async Task<TriggerResult> CustomAsyncTrigger(EntityModel model, List<object> entities, TriggerExecutionContext context, dynamic options)
{
return TriggerResult.Success();
}
private TriggerResult CustomTrigger(EntityModel model, List<object> entities, TriggerExecutionContext context, dynamic options)
{
return TriggerResult.Success();
}
#endregion
#region Actions
private async Task<object> CustomAsyncAction(dynamic request)
{
return null;
}
private Task<object> CustomAsyncAction(dynamic request)
{
return null;
}
#endregion
}
Triggers, Filters and Actions work in a similar way here. Get***Names
functions return lists of Triggers, Filters or Actions
names to be displayed in the admin panel. Is***Async
functions return true if Trigger, Filter or Action must be executed
asynchronously. Contains***
functions check whether Trigger, Filter or Action is contained in the action provider.
GetFilter
andGetFilterAsync
functions return filters with the givenname
.ExecuteTrigger
andExecuteTriggerAsync
functions execute a trigger with the givenname
.ExecuteAction
andExecuteActionAsync
functions execute an action with the givenname
.
After ActionsProvider
is created, you should register it in DWKitRuntime
.
DWKitRuntime.ServerActions.RegisterUsersProvider("provider", new ActionsProvider());
Connection between the EntityModel
model and the Triggers, Filters or Actions calling can be adjusted in the admin panel. For
example, Triggers can be specified in the Triggers section. Filters are specified in the url so
far. For example, if you open the https://demo.dwkit.com/form/documents/outbox
page, DWKit will parse this url as -
formName='documents', filter='outbox' and try to call a user-defined action of the filter type with the 'outbox' name.