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:

Code Actions

Control elements in this interface have the following functions:

  1. Link to the list of all server actions registered in the system.
  2. Opened server action name.
  3. Compile - button to test action compilation. If there are any errors in the code, they will be displayed in the bottom of this form.
  4. Save - Save button.
  5. Cancel - Button to cancel all changes.
  6. Name - server action name is to be unique in the system. DWKit call server action by this name.
  7. Type - server action type: Trigger, Filter or Action.
  8. Is Async - if this checkbox is checked, action will be called asynchronously.
  9. Usings - list of usings, required for code compilation. Must be separated by the ';' symbol.
  10. Comment - comment to action, non-required.
  11. Server Action method signature. Depends on Type and Is Async. Is included for info purposes.
  12. 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 and GetFilterAsync functions return filters with the given name.
  • ExecuteTrigger and ExecuteTriggerAsync functions execute a trigger with the given name.
  • ExecuteAction and ExecuteActionAsync functions execute an action with the given name.

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.