Understanding the FactHandle in a FactRetriever

by Matt Milner 4. January 2006 15:23
One of the most confusing things, other than that pesky document type problem, that developers I work with run into with the business rules engine in BizTalk is understanding the role of the fact handle when implementing a fact retriever. 
 
A FactHandler, for those that are not familiar, is a class that one implements in order to supply long term facts to the Business Rule Engine in BizTalk Server.  Common items returned include database connections or tables, or xml content from a database or file.  The business rule engine calls the fact handler on each invocation of any policy it is configured on to get these facts.  It is the job of the fact retriever to make sure that it provides up to date information as appropriate and caches data or variables where necessary. 
 
It would seem, from the name of the variable, "factHandle", and the way it is passed to the single function in the IFactRetriever interface, that this object is actually the fact(s) being returned by the fact handler.  In reality, however, this object is actually the item that helps the fact retriever be smart about caching and retrieving data.  The act of getting facts into the rule engine occurs by Asserting items into the engine within the fact retriever method. 
 
The fact handle is the object that your fact retriever creates the first time it is called.  This object is then passed back in to your fact retriever each time the retriever is called so that it can use this information to determine if it needs to refetch data or just allow the business rule engine to use the data it returned last time.  A simple, yet effective example, is to return a DateTime struct on the first request and then, on subsequent calls, check it against a configured timeout to determine if you should fetch data again. 
 
The code below shows both the UpdateFacts method as well as a helper method to determine if the facts are out of date. 
 
public object UpdateFacts(RuleSetInfo ruleSetInfo, RuleEngine engine, object factsHandleIn)
{
  object factsHandleOut = null;
  if(factsHandleIn == null || FactsAreOutOfDate(factsHandleIn))
  {
    //Assert facts into the engine at this point using the "engine" parameter
 
    //If this is the first time here, or we are out of date
    //then update the facts handle to the current time
    factsHandleOut = DateTime.Now;
  }
else
{
  factsHandleOut = factsHandleIn;
}
  return factsHandleOut;
}
 
 
private bool FactsAreOutOfDate(object factsHandle)
{
  DateTime lastUpdateTime;
  if(factsHandle == null)
     return true;
  if(factsHandle is DateTime)
  {
    lastUpdateTime = (DateTime)factsHandle;
    return (TimeSpan.Compare(DateTime.Now.Subtract(lastUpdateTime),
                    TimeSpan.FromDays(1)) > 0);
  }
  else
    return true;
}
 
In the first method, we test for the fact handle to be null or to be out of date.  We do a simple check to see if the facts are more than a day old.  If they are, then we return true so that we know to reassert the facts. Back in the UpdateFacts method, we either return the original facts handle if we don't want to make any changes, or we return the current time if we are asserting new facts and want to reset our "timer". 
 
This is obviously a simple example and might be extended by using a class or struct to wrap multiple DateTime structs representing different facts. That is, we might not want to treat all of the facts with the same policy, so we can have timeouts for different facts.  A database connection we might not refresh, but a datatable we might refresh every few hours. 
 
Hopefully, this information will be helpful to people trying to work with the business rules engine in BizTalk. 
 

Tags:

BizTalk Server