Intercepting Queries
LightSpeed provides a facility to intercept interactions with the ADO.NET provider and hooks within the querying pipeline to allow you to either inspect the query and ADO.NET objects for logging or profiling purposes or to manipulate them to apply security or filtering concerns.
Intercepting ADO.NET interactions
If you require an awareness of the interactions that LightSpeed takes with ADO.NET Firstly a generic interceptor class which if set will give you access to the ADO.NET interactions. To create an interceptor you must derive from the Interceptor base class in the Mindscape.LightSpeed.Profiling namespace and then implement override methods as required.
Here is an example of using this approach to couple with the ASP.NET Mini Profiler:
Implementing a custom Interceptor class |
public class MiniProfilerInterceptor : Interceptor |
To attach an interceptor either assign a new instance to the Interceptor property on your LightSpeedContext instance, or set it via the context configuration.
Assigning an interceptor by configuration |
<lightSpeedContexts> |
Intercepting LightSpeed queries
Sometimes entities come with a ‘natural’ filter — a filter that should be applied by default to all queries, because the database contains stuff that users shouldn’t normally see. The classic example, which is built into the LightSpeed framework, is soft deletion: entities can be kept in the database but marked as logically deleted, and ‘deleted’ entities shouldn’t be returned from queries. But there are other common use cases for this which aren’t built into LightSpeed, including liveness, currency and security filters.
For example:
· In a membership system, you might want queries to consider only active members (liveness).
· In a system where data has a time range during which it is valid, you might want queries to consider only data that are valid at a particular time (currency).
· In a case management system, users should be able to see only cases that they are working on (security).
To solve these requirements you can implement query based interception filters, which allow you to add filter criteria declaratively at the entity level.
Declaring how an entity type should be filtered
To declare that an entity type should be automatically filtered, apply the QueryFilterAttribute. QueryFilterAttribute takes a type, which must implement the IQueryFilter interface.
[QueryFilter(typeof(IsActiveFilter))] |
Filtering by interface
In models where implicit filters make sense, it’s likely that many entities in the model will need similar filters. For example, if several entity types have time ranges during which they’re valid, you’ll probably want to apply the same currency filter to all of them. To support this, LightSpeed allows you to specify QueryFilterAttribute on an interface, and applies the filter to all types that implement that interface. Here’s an example:
[QueryFilter(typeof(IsCurrentFilter))] |
With this idiom the interface becomes a powerful declarative tool for specifying query behaviour, and the query filter implementation can assume the properties in the interface are available for querying on.
Implementing a query filter
Now we know how to specify an implicit filter on an entity, either directly or indirectly through an interface, how do we turn that into an actual query? A filter must implement the IQueryFilter interface which is described as:
IQueryFilter interface definition |
public interface IQueryFilter |
To implement this you must return a LightSpeed QueryExpression object that consults the appropriate entity property. For example if the database table contains an IsActive flag which we want to filter on then you might write a query expression such as the following:
public class IsActiveFilter : IQueryFilter |
If you didn’t explicitly model IsActive, but wanted to implicitly archive all members who hadn’t logged in for, say, 180 days, your filter would look a bit more complex, but it would still be a familiar query expression object:
public class IsActiveFilter : IQueryFilter |
Injecting values into implicit filters
By default, when LightSpeed runs a query on an entity with an implicit filter, it instantiates the IQueryFilter object using its default constructor. This is fine for the IsActive case where there’s nothing to parameterise, but it may be more problematic for the security scenario, where the query filter will depend on the logged-in user. In most cases, this will be available through some global mechanism — in a Web application, for example, the filter could use the HttpContext.Current.User — but sometimes it will be useful to inject data into a filter or otherwise control its behaviour on a per-instance basis.
To do this, you need to use an Interceptor and override the CreateQueryFilter method. This allows you full control over the creation and initialisation of the IQueryFilter object
Turning off filtering
Occasionally you will want to turn off an implicit filter, for example in an admin interface where you want to be able to work with inactive users or have permission to view all cases. To do this, just return null from your IQueryFilter.Filter implementation. You’ll need to implement some way of signalling to your IQueryFilter when it should return null — currently you can do this using a global flag or via an ADO.NET based interceptor.