Sunday, September 14, 2008

Server-Side Data Filters

Data Aquarium Framework and Aquarium Express applications are featuring user-defined adaptive filtering. Users can select any combination of field values in dynamic menus of grid view columns to find the data they need.

Take a look at a live example of Northwind database web application available at http://dev.codeontime.com/demo/northwind. The screen shot displays a list of orders filtered down to employee with last name Fuller. User is about to apply an additional filter to the column Ship Via Company Name.

image

If you want your users to see only a subset of data then define alternative data views based on queries that are limiting data in the SQL query in the foundation of each views.  The advantage of this approach is an ability to quickly define a data subset limited by a known criteria. Dynamic parameter values are not allowed in the query.

What if you want to limit the list of orders to those placed by currently signed-in employee only?

New feature available in Data Aquarium Framework allows to define a reusable data filter class used by linked data controllers automatically whenever a corresponding data retrieval operation requires a  filter. Let's see how it works.

Generate a web site code with Data Aquarium Framework project  and Northwind database.  Open this web site in Visual Studio 2008 or free Visual Web Developer 2008 Express Edition. Add Class1 to App_Code folder and enter the following code.

Data filter written in C#:

using System;
using System.Collections.Generic;
using MyCompany.Data;

public class Class1 : IDataFilter
{
    void IDataFilter.Filter(SortedDictionary<string, object> filter)
    {
        filter.Add("EmployeeID", new int[] { 2, 1 });
        filter.Add("ShipVia", 1);
    }
}

Here is VB.NET version of this class:

Imports System.Collections.Generic
Imports MyCompany.Data

Public Class Class1
    Implements IDataFilter

    Public Sub Filter(ByVal filter As SortedDictionary(Of String, Object)) _
        Implements MyCompany.Data.IDataFilter.Filter
        filter.Add("EmployeeID", New Object() {2, 1})
        filter.Add("ShipVia", 1)
    End Sub
End Class

Open data controller descriptor ~/Controllers/Orders.xml and  add an attribute dataFilterType as shown below:

<dataController 
  name="Orders" conflictDetection="overwriteChanges" 
  label="Orders" xmlns="urn:schemas-codeontime-com:data-aquarium" 
  dataFilterType="Class1">

If you run the web site and select Orders in the drop down of the default page then a view like the one below is displayed.

image

Only eight pages of orders are available. All of them are limited to those placed by employees Fuller and Davolio. All of these orders were shipped via Speedy Express.  If you look at the sample code then you will notice that we are supplying filter values for EmployeeID field as an array with two values, while the ShipVia field is filtered by a single value only.

A quick and easy enhancement will make your view less busy. In our example only one shipper is displayed. You can hide this column from user interface by adding an additional hidden attribute to dataField element in the data controller descriptor ~/Controllers/Orders.xml.

<dataField fieldName="ShipVia" aliasFieldName="ShipViaCompanyName"  hidden="true"/>

In a real-world scenario you will likely use HttpContext.Current.User.Identity property or HttpContext.Current.User.IsInRole method to determine the scope of data that you want to include in the result set displayed to a currently signed in user. Make sure to add System.Web namespace to the list of namespaces in your data filter to get access to HttpContext.Current property.

What do you do if the number of filter values is greater than just a few? In this case you might want to consider creating a database table that holds sets of filter values matched with user identities or roles and then join this table into SQL queries defined in corresponding data controller descriptors. Implement a server-side data filter to provide an ID or a role of current user.

If the name of the data filter field is not matched with any of the fields in the result set then the value is ignored. You can link the same filter to multiple data controllers to take advantage of centralized processing architecture of Data Aquarium Framework.

8 comments:

Lloyd Merithew said...

Can you provide an example of this?

In a real-world scenario you will likely use HttpContext.Current.User.Identity property or HttpContext.Current.User.IsInRole method to determine the scope of data that you want to include in the result set displayed to a currently signed in user. Make sure to add System.Web namespace to the list of namespaces in your data filter to get access to HttpContext.Current property.

oumiller21 said...

Where is the IDataFilter class located. I downloaded the latest version of the CodeGenerator and I Don't have access to the Interface.

Thanks :)

Code OnTime said...

Server-side data filters are supported in Data Aquarium Framework, which is available to premium project subscribers only.

Pravin said...

Can you please show me the code where you may have created a instance to the class "Class1" and passed filters to "EmployeeId" and "ShipVia" fields.

Code OnTime said...

Pravin,

You don't have to create an instance of Class1 explicitly. Simply modify dataController element to include the class name in dataFilterType attribute:

<dataController name="Orders" ... dataFilterType="Class1">

The class instance is automatically created when data is about to be returned to the client. You can communicate with the class via Session object.

For example, in your *.aspx page assign values to session variables EmployeeID and ShipVia:

Protected Sub Button1_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Session("EmployeeID") = 45
Session("ShipVia") = 2
End Sub

Use session variables as in this example:

Imports System.Collections.Generic
Imports MyCompany.Data

Public Class Class1
Implements IDataFilter

Public Sub Filter(ByVal filter As SortedDictionary(Of String, Object)) _
Implements MyCompany.Data.IDataFilter.Filter
filter.Add("EmployeeID", HttpContext.Current.Session("EmployeeID"))
filter.Add("ShipVia", HttpContext.Current.Session("ShipVia"))
End Sub
End Class

Pravin said...

I have tried creating IDataFilter interface in Common.vb

Public Interface IDataFilter
Sub Filter(ByVal filter As SortedDictionary(Of String, Object))
End Interface

And then I created Class1 following your tutorial and also made change to the controller.xml file, also tried to pass the values using session variables. But still no filteration is available.

Is the files created using your premium subscription is different from that created using your free version?

ajnar said...

Tried to load the Class1 but still no filtering. Where exactly is the code added to,i added it to my page codebehind but it doesnt seem to be called?

public class Class1 : IDataFilter
{

public void Filter(SortedDictionary filter)
{
int FormNo;
if (HttpContext.Current.Session["FormNo"] == null)
{
FormNo = Convert.ToInt32('8');
}
else
{
FormNo =Convert.ToInt32(HttpContext.Current.Session["FormNo"]);

}
filter.Add("FormNo", FormNo);

}
}

Customer Service said...

You need to link your class to the data controller through "handler" attribute.

We suggest that you consider using view filters instead as described at http://blog.codeontime.com/2010/05/sort-and-filter.html

You can find more about Code OnTime Generator, Data Aquarium Framework, and other great products here.


© 2010 Code OnTime LLC. Intelligent code generation software for ASP.NET. Visit us at http://codeontime.com