Share via


Message Filters and Queries

Since a message filter and message query share a similar heritage, let's start by looking at the conceptually simpler message filter APIs. You probably haven't seen message filters before unless you've gone out of your way to explore everything that comes with WCF. They don't appear in the ordinary use of web services.

A message filter is basically a matching delegate that works on WCF messages. There is not a lot of interesting message filter methods.

 public abstract class MessageFilter
{
   protected internal virtual IMessageFilterTable<FilterData> CreateFilterTable<FilterData>();
   public abstract bool Match(Message message);
   public abstract bool Match(MessageBuffer buffer);
}

As you might have guessed, there are Match methods for both messages and message buffers. There's also this message filter table that you might not have expected. The message filter table is used to optimize execution of message filters. A message filter table contains a collection of related message filters and also has a slot for the application to store state data with each filter.

 public class MessageFilterTable<TFilterData> : IMessageFilterTable<TFilterData>, IDictionary<MessageFilter, TFilterData>, ICollection<KeyValuePair<MessageFilter, TFilterData>>, IEnumerable<KeyValuePair<MessageFilter, TFilterData>>, IEnumerable
{
   public bool GetMatchingFilter(Message message, out MessageFilter filter);
   public bool GetMatchingFilter(MessageBuffer buffer, out MessageFilter filter);
   public bool GetMatchingFilters(Message message, ICollection<MessageFilter> results);
   public bool GetMatchingFilters(MessageBuffer buffer, ICollection<MessageFilter> results);
   public int GetPriority(MessageFilter filter);
}

I've picked a subset of the methods that help show how you can use the message filter table abstraction to change how the message filters execute. For example, let's say that you've got a bunch of message filters in a message filter table and two of the filters share some common work. When you execute the batch of message filters through the message filter table interface, the shared common work would ideally only get executed once. Hiding the message filters behind the message filter table abstraction allows for these types of optimizations because the black box prevents an external observer from seeing the actual computations that get done. The priority scheme is just an addon to deal with the fact that multiple message filters may match the same message.

A message query looks almost the same as a message filter except that message queries generate results instead of matches.

 public abstract class MessageQuery
{
   public virtual MessageQueryCollection CreateMessageQueryCollection();
   public abstract TResult Evaluate<TResult>(Message message);
   public abstract TResult Evaluate<TResult>(MessageBuffer buffer);
}

The message query table similarly replaces results with matches. I've again picked a subset of methods that demonstrate this. You'll also notice that message queries are throughout emphasizing a multiple match mode rather than the single match mode of the message filter table. This is due to the slightly different use cases that the two were designed for.

 public class MessageQueryTable<TItem> : IDictionary<MessageQuery, TItem>
{
   public IEnumerable<KeyValuePair<MessageQuery, TResult>> Evaluate<TResult>(Message message);
   public IEnumerable<KeyValuePair<MessageQuery, TResult>> Evaluate<TResult>(MessageBuffer buffer);
}