Filtering Lookup Tables in the Business Class Layer

"Chances are you have written the same code over and over again to filter dropdown lists by the logged-in user ID. Situations such as this are exactly what the Business Layer is for."
- Michael Weiss, Independent Application Architect and Developer

December 6, 2006
Iron Speed Designer V4.0

Introduction

If your application has to provide users with the ability to enter their own lookup data such as expense types, clients and client types, chances are you have written the same code over and over again to filter dropdown lists by the logged-in user ID. Situations such as this are exactly what the Business Layer is for. In this article we look at how to write the same code once and then apply it for each lookup table you want filtered.

Table Design

For this article, let’s assume our lookup tables all contain a column to hold the unique ID of the user or owner of a record in the table. In our lookup tables this column is called ‘UsrKey’ and will be the column used for filtering the table. Let’s use a table called ExpenseTypes. Our ExpenseTypes table might look like this:

Field Data Type Notes
ExpenseTypeID Integer Primary Key
ExpenseType VarChar  
UsrKey Integer Unique id of logged-in user

The Code

The filtering code goes into the Business Layer of our application and we will accomplish the filtering by overriding three functions. We will look at overriding those functions in a moment. First, let’s locate the correct file and class to work with.

In the Application Explorer, select the Business Layer node to expand it. The Business Layer contains a folder for every table, view, and custom query in your application. For our purpose we are looking for the folder ‘ExpenseTypes’.


ExpenseTypes table code in the Business Layer.

Selecting the ExpenseTypesTable.cs file under the ExpenseTypes node. Clicking this file opens the ExpenseTypesTable.cs file in the Code window in Iron Speed Designer.

The code comments tell us this is a “safe” class, meaning we can place code here without worrying it will be overwritten by Iron Speed Designer when we compile our application.


ExpenseTypes table code displayed in the Business Layer.

To get to the table class you will scroll past the [TableName]TableRecord class. As a side note this is where you would place code that needs to execute with the loading of each record in an Add, Edit or Show table control. We are going to place our code in the table class. Whatever we put in the table class will modify the ExpenseTypes table class’s behavior application wide.


Locating the proper place to insert code to override the ExpenseTypesTable class.

As mentioned above, to filter this data table by the authenticated user we need to override three methods of the ExpenseTypes table class. The functions we are concerned with are:

  1. GetRecordList()
  2. GetRecordListCount()
  3. GetColumnStatistics()
Each of these three functions takes as one of their arguments a filter object. This filter object is of the type BaseFilter. Using the debugger in Visual Studio 2005 we see the actual filter being passed into our functions is of the subclass CompoundFilter. This CompoundFilter object has a method called AddFilter which we can use to append our UserID filter to the filter being passed in to the function.


The subclass of the filter argument is of type BaseClasses.Data.CompoundFilter.

We override the GetRecordList function in the ExpenseTypesTable class by adding the following code just below the private ExpenseTypesTable()method:

public override System.Collections.ArrayList GetRecordList(BaseFilter filter, OrderBy orderBy,
        int pageIndex, int pageSize)
{
    DataAccessSettings dataAccessSettingsObj = DataAccessSettings.Current;

    BaseClasses.Data.ColumnValueFilter f =
        new ColumnValueFilter(UsrKeyColumn,dataAccessSettingsObj.SignedInUserId.ToString(),
                        BaseFilter.ComparisonOperator.EqualsTo, false);

    BaseClasses.Data.CompoundFilter myFilter = (BaseClasses.Data.CompoundFilter)filter;

    myFilter.AddFilter(f);

    return base.GetRecordList(myFilter, orderBy, pageIndex, pageSize);
}

First, after gaining a hook to the current DataAccessSettings object we pass the logged-in user’s ID from its SignedInUserId property to a new ColumnValueFilter object, f. We append our new filter f to the compound filter being passed in to the GetRecordList function and pass the combined filter on to the base GetRecordList function. base.GetRecordList(myFilter, orderBy, pageIndex, pageSize) then returns an array list of the records filtered by the authenticated user’s ID as well as any other filters the user may have selected when requesting the record set.

To complete our task, we override the GetRecordListCount and GetColumnStatistics functions by placing the following code below our override of the GetRecordList method.

public override long GetRecordListCount(BaseFilter filter, OrderBy orderBy)
{
    DataAccessSettings dataAccessSettingsObj = DataAccessSettings.Current;

    BaseClasses.Data.ColumnValueFilter f = new ColumnValueFilter
            (UsrKeyColumn,dataAccessSettingsObj.SignedInUserId.ToString(),
            BaseFilter.ComparisonOperator.EqualsTo, false);

    BaseClasses.Data.CompoundFilter myFilter = (BaseClasses.Data.CompoundFilter)filter;

    myFilter.AddFilter(f);

    return base.GetRecordListCount(myFilter, orderBy);
}

public override string GetColumnStatistics(SqlBuilderColumnSelection selectCol, BaseFilter filter,
        OrderBy orderBy, int pageIndex, int pageSize)
{
    DataAccessSettings dataAccessSettingsObj = DataAccessSettings.Current;

    BaseClasses.Data.ColumnValueFilter f = new ColumnValueFilter
    (UsrKeyColumn,dataAccessSettingsObj.SignedInUserId.ToString(),
    BaseFilter.ComparisonOperator.EqualsTo, false);

    BaseClasses.Data.CompoundFilter myFilter = (BaseClasses.Data.CompoundFilter)filter;

    myFilter.AddFilter(f);

    return base.GetColumnStatistics(selectCol, myFilter, orderBy, pageIndex, pageSize);
}


Completed code modifications for the ExpenseTypesTable partial class in the business layer.

That is all there is to it! The following screen shots show the dropdown list for expense type on an Add Record page before and after applying our modifications.



Our expense types table prior to applying our modifications of the business layer. Total record count is fourteen. Note the duplicate entries for ‘Mileage’ and ‘Payroll’. This reflects duplicate expense type entries for each of two users.



Our expense types table control after applying our modifications to the business layer. Note the row count is now eleven, reflecting proper filtering for those records coded to the logged-in user only. Only one Mileage and Payroll entry shows now.

As a final note, you may have users who need to see all records in the lookup table regardless of the user ID. For example, application administrators potentially need access to all client records regardless of user. In this case you could wrap the filtering code in a conditional test for the logged-in user’s security role.

In future articles we will take a look at filtering all tables across the entire application by a common value as well as modeling additional business rules in the business layer.

About the Author

Michael Weiss
Independent Developer

Michael has over ten years of experience in information technology primarily in the Telecom sector. He has designed, developed and administered data warehouses to 500GB in size, web enabled reporting systems and billing and CRM systems. Additionally, Michael has designed and developed several web enabled OLAP-based Business Intelligence Reporting systems.

Contact the author.



  Privacy Statement