Search Results for

    Show / Hide Table of Contents

    Global Filters

    With Aurelius you can define filters that are applied to several entities at once.

    You can specify the filters using the FilterDef attribute once in any entity of your model, then add a Filter attribute for each entity you want to be filtered. For example:

      [Entity, Automapping]
      [FilterDef('Multitenant', '{TenantId} = :tenantId')]
      [FilterDefParam('Multitenant', 'tenantId', TypeInfo(string))]
      [Filter('Multitenant')]
      TProduct = class
      private
        FId: Integer;
        FName: string;
        FTenantId: string;
      public
        property Id: Integer read FId write FId;
        property Name: string read FName write FName;
        property TenantId: string read FTenantId write FTenantId;
      end;
    

    And finally, enable filters at the TObjectManager level, using EnableFilter method:

      Manager.EnableFilter('Multitenant')
        .SetParam('tenantId', 'microsoft');
      Products := Manager.Find<TProduct>.OrderBy('Name').List;
    

    Once you do that, the SELECT statement built by Aurelius to retrieve the products will include the filter condition specified in the attribute. The above code would generate an SQL statement like this:

    SELECT A.NAME, A.ID, A.TENANT_ID
      FROM PRODUCT A
      WHERE A.TENANT_ID = :p0
      ORDER BY A.NAME;
    
    :p0 = 'microsoft'
    

    This will apply for all entities with the Multitenant filter defined, even if they are associations.

    This makes it very easy to build multitenant applications, for example. You don't have to worry about adding filters to every Aurelius query you build. Just code it with the regular business logic, and Aurelius global filter will take of adding the filters that apply globally to all entities.

    Creating filter definitions

    To create a filter definition, use the FilterDef and FilterDefParam attributes:

      [FilterDef('Multitenant', '{TenantId} = :tenantId')]
      [FilterDefParam('Multitenant', 'tenantId', TypeInfo(string))]
      TProduct = class
    

    The first parameter of FilterDef attribute is the filter name. The second parameter is optional, and contains the filter condition that will be applied to all entities that don't explicitly add a filter condition in their Filter attribute.

    Note

    Filter definitions are global to a model. Do not add two or more FilterDef attributes with the same filter name, even in different entities

    Filter conditions and parameters

    Filter conditions are SQL condition expressions that are added to the WHERE clause of any SELECT statement used to retrieve entities. They will also be applied when the entity is being queried as an associated object.

      [FilterDef('Deleted', '{Deleted} = 0')]
    

    You should use aliases to refer to database columns, with the class field (or property) name wrapped by brackets ({ }), in the same you will use aliases in SQL conditions .

    Filter conditions can also use parameters, that are defined by prefixing the parameter name with : (colon). In the following example, tenantId is a parameter.

      [FilterDef('Multitenant', '{Multitenant} = :tenantId')]
    

    For each parameter in the filter condition, you must explicitly add a FilterDefParam attribute in addition to the FilterDef attribute, specifying the Delphi type of the parameter. In this example, tenantId parameter is of type string:

    [FilterDefParam('Multitenant', 'tenantId', TypeInfo(string))]
    

    First argument of FilterDefParam is the filter name, second is the parameter name, third is the Delphi type of the parameter.

    Applying filters to entities

    Once a filter is defined, you can specify which entities might have such filter applied. This is as simple as adding a Filter attribute to any entity you want the filter to be applied, specifying the filter name as the first argument:

      [Filter('Multitenant')]
      [Filter('Deleted')]
      TProduct = class
    
    {...}
    
      [Filter('Multitenant')]
      TCustomer = class
    

    In the above example, filters Multitenant and Delete will be applied to entity Product when they are enabled. On the other hand, TCustomer entity will only have filter Multitenant applied to it. Filter Deleted will have no effect on entity Customer.

    Warning

    If you are using an inheritance strategy, you should only apply filters to then entity class which is the root of the class hierarchy. Do not apply the filter in a descendant class.

    If a filter is applied to a descendant class in a class hierarchy, Aurelius might bring wrong results if such classes as retrieved as associated objects.

    You can also override the default filter condition specified in the filter definition, by passing a new filter condition as the second parameter:

      [Filter('Multitenant', {AnotherTenantId} = :tenantId)]
      TOtherClass = class
    

    The filter condition must use the same parameters specified in the filter definition.

    Enabling filters

    Finally, filters are enabled at the TObjectManager level, using EnableFilter method.

      Manager.EnableFilter('Delete');
      Products := Manager.Find<TProduct>.OrderBy('Name').List;
    

    Once you enable a filter for a specific object manager, all SQL query statements executed by that manager will have the filter condition added to the WHERE clause when searching for the involved entity(ies).

    Warning

    Filters are always disabled by default. If you don't call EnableFilter to enable a specific filter, no conditions are applied to any entity using that filter!

    If the filter definition includes a condition that uses parameters, you must set the value of each parameter of the filter using SetParam method:

      Manager.EnableFilter('Multitenant')
        .SetParam('tenantId', 'microsoft');
    

    You can also disable a filter using DisableFilter method and check if a filter is enabled using FilterEnabled:

      if Manager.FilterEnabled('Multitenant') then
        Manager.DisableFilter('Multitenant');
    

    Filter enforcer

    When you enable a filter, it "only" applies the SQL WHERE condition to SELECT statements, making all your data automatically filtered. But it doesn't do anything when you insert, update or delete a database record.

    Fortunately, Aurelius provides mechanism named "filter enforcer" which does that for you: it makes sure that whenever you modify entity data (create, update, delete), it will be consistent with the condition specified in the filter.

    In other words, considering the Multitenant filter example: if you have enabled the Multitenant filter to retrieve data when tenantId is equals to microsoft, the filter enforcer will make sure that you also don't create, update or delete an entity if TenantId property is not microsoft.

    Use the following code to activate the filter enforcer:

    uses {...}, Aurelius.Mapping.FilterEnforcer;
    
    // variable/class field declaration
    Enforcer: TFilterEnforcer;
    
    // creating and activating filter enforcer
    Enforcer := TFilterEnforcer.Create('Multitenant', 'TenantId', 'FTenantId');
    Enforcer.Activate(TMappingExplorer.Default);
    
    // deactivating and destroying filter enforcer
    Enforcer.Deactivate(TMappingExplorer.Default);
    Enforcer.Free;
    

    You can see that the TFilterEnforcer class constructor receives three parameters: The first is the filter name (Multitenant), the second is the name of a filter condition parameter, the third is the name of the class member (field or property) that will be checked against the parameter value.

    Also, when you activate a filter enforcer, you must specify to which mapping explorer (model) it will be applied. The above example is using the default model.

    The enforcer will subscribe to key model events. Whenever an entity is about to be inserted, updated or deleted, the enforcer will check if:

    1. If the specified filter is active (Multitenant);
    2. If yes, check if the value of specified class member (FTenantId) matches the value of filter parameter (tenantId).

    If the condition 2 above is tested and fails, an exception will be raised. Optionally you can ask the enforcer to auto comply to the value in either insert and/or update operations:

    Enforcer.AutoComplyOnInsert := True;
    Enforcer.AutoComplyOnUpdate := True;
    

    When the two properties above are enabled, the enforcer will check if the class member (FTenantId) is empty. If it is, then instead of raising an error when the filter is enabled, it will automatically set the member value using the parameter value. In other words: if a Multitenant filter is active with tenantId parameter set to microsoft, if you try to insert or update a record without specifying the TenantId property, the enforce will automatically fill it with the microsoft value for you.

    In This Article
    Back to top TMS Aurelius v5.11
    © 2002 - 2022 tmssoftware.com