How to Create a Fact Retriever

A fact retriever is a component that is used to assert instances of long-term facts into a policy during its execution. You can implement the IFactRetriever interface and configure a policy version to use this implementation at run time to bring in the long-term fact instances. The policy version invokes the UpdateFacts method of the fact retriever implementation on every execution cycle, if a fact retriever is configured for that particular version.

You can optionally implement the IFactRemover interface on a fact retriever component. The rule engine invokes the UpdateFactsAfterExecution method of the IFactRemover interface when the policy is disposed. This provides an opportunity to you to do any post-execution work such as committing any database changes or retracting any object instances from the rule engine's working memory.

To specify a fact retriever for a policy

You could use the following code to configure the rule set to use a class named "Retriever" in the assembly named "MyAssembly" as the fact retriever.

RuleEngineComponentConfiguration fr = new RuleEngineComponentConfiguration("MyAssembly", "Retriever");
RuleSet rs = new RuleSet("ruleset");
// associate the execution configuration with a ruleset
RuleSetExecutionConfiguration rsCfg = rs.ExecutionConfiguration;
rsCfg.FactRetriever = factRetriever;

Note

If you specify simple assembly name such as MyAssembly as the first parameter for RuleEngineComponentConfiguration constructor, the BizTalk rule engine assumes that it is a private assembly and looks for the assembly in your application folder. If you specify fully qualified assembly name such as MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a310908b42c024fe, the rule engine assumes that it is a shared assembly and looks for the assembly in the global assembly cache (GAC). You can find the definitions of simple and fully qualified assembly names at https://go.microsoft.com/fwlink/?LinkId=64535.

You can design the fact retriever with the required application-specific logic to connect to the required data sources, assert the data as long-term facts into the engine, and specify the logic for refreshing or asserting new instances of the long-term facts into the engine. Until updated, the values that are initially asserted into the engine and consequently cached will be used on subsequent execution cycles. The fact retriever implementation returns an object that is analogous to a token and can be used along with the factsHandleIn object to determine whether to update existing facts or assert new facts. When a policy version calls its fact retriever for the first time, the factsHandleIn object is always null and then takes the value of the return object after the fact retriever's execution.

Note that for the same rule engine instance, a long-term fact only needs to be asserted once. For example, when you use the Call Rules shape in an orchestration, the policy instance is moved into an internal cache. At this time, all short-term facts are retracted and long-term facts are kept. If the same policy is called again, either by the same orchestration instance or by a different orchestration instance in the same host, this policy instance is fetched from the cache and reused. In some batch processing scenarios, several policy instances of the same policy could be created. If a new policy instance is created, you must ensure that the correct long-term facts are asserted.

Additionally, you would need to write custom code to implement the following strategies:

  • Know when to update the long-term facts

  • Keep track of which rule engine instance uses which long-term facts

    The following sample code shows different fact retriever implementations, which are associated with MyPolicy to assert MyTableInstance as a long-term fact, using different binding types.

DataTable binding

using System;
using System.Xml;
using System.Collections;
using Microsoft.RuleEngine;
using System.IO;
using System.Data;
using System.Data.SqlClient;
namespace MyBizTalkApplication.FactRetriever
{
   public class myFactRetriever:IFactRetriever
   {
      public object UpdateFacts(RuleSetInfo rulesetInfo, Microsoft.RuleEngine.RuleEngine engine, object factsHandleIn)
      {
         object factsHandleOut;
         if (factsHandleIn == null)

         {
            SqlDataAdapter dAdapt = new SqlDataAdapter();
            dAdapt.TableMappings.Add("Table", "CustInfo");
            SqlConnection conn = new SqlConnection("Initial Catalog=Northwind;Data Source=(local);Integrated Security=SSPI;");
            conn.Open();
            SqlCommand myCommand = new SqlCommand("SELECT * FROM CustInfo", conn);
            myCommand.CommandType = CommandType.Text;
            dAdapt.SelectCommand = myCommand;
            DataSet ds = new DataSet("Northwind");
            dAdapt.Fill(ds);
            TypedDataTable tdt = new TypedDataTable(ds.Tables["CustInfo"]);
            engine.Assert(tdt);
            factsHandleOut = tdt;
         }

         else
            factsHandleOut = factsHandleIn;
         return factsHandleOut;
      }
   }
}

DataRow binding

using System;
using System.Xml;
using System.Collections;
using Microsoft.RuleEngine;
using System.IO;
using System.Data;
using System.Data.SqlClient;
namespace MyBizTalkApplication.FactRetriever
{
   public class myFactRetriever:IFactRetriever
   {

      public object UpdateFacts(RuleSetInfo rulesetInfo, Microsoft.RuleEngine.RuleEngine engine, object factsHandleIn)
      {
         object factsHandleOut;
         if (factsHandleIn == null)

         {
            SqlDataAdapter dAdapt = new SqlDataAdapter();
            dAdapt.TableMappings.Add("Table", "CustInfo");
            SqlConnection conn = new SqlConnection("Initial Catalog=Northwind;Data Source=(local);Integrated Security=SSPI;");
            conn.Open();
            SqlCommand myCommand = new SqlCommand("SELECT * FROM CustInfo", conn);
            myCommand.CommandType = CommandType.Text;
            dAdapt.SelectCommand = myCommand;
            DataSet ds = new DataSet("Northwind");
            dAdapt.Fill(ds);
            TypedDataTable tdt = new TypedDataTable(ds.Tables["CustInfo"]);

            // binding to the first row of CustInfo table
            TypedDataRow tdr = new TypedDataRow(ds.Tables["CustInfo"].Rows[0],tdt);
            engine.Assert(tdr);
            factsHandleOut = tdr;
         }
         else
            factsHandleOut = factsHandleIn;
         return factsHandleOut;
      }
   }
}

The following sample code demonstrates how to assert .NET and XML facts in a fact retriever implementation.

using System;
using System.Xml;
using System.Collections;
using Microsoft.RuleEngine;
using System.IO;
using System.Data;
using System.Data.SqlClient;
namespace MyBizTalkApplication.FactRetriever
{
   public class myFactRetriever:IFactRetriever
   {
      public object UpdateFacts(RuleSetInfo rulesetInfo, Microsoft.RuleEngine.RuleEngine engine, object factsHandleIn)
      {
         object factsHandleOut;
         if (factsHandleIn == null)
         {
            //create .NET object instances
            bookInstance = new Book();
            magazineInstance = new Magazine();

            //create an instance of the XML object
            XmlDocument xd = new XmlDocument();

            //load the document
            xd.Load(@"..\myXMLInstance.xml");

            //create and instantiate an instance of TXD
            TypedXmlDocument doc = new TypedXmlDocument("mySchema",xd1);

            engine.Assert(bookInstance);
            engine.Assert(magazineInstance);
            engine.Assert(doc);
            factsHandleOut = doc;
         }
         else
            factsHandleOut = factsHandleIn;
         return factsHandleOut;
      }
   }
}

DataConnection Binding

using System;
using System.Xml;
using System.Collections;
using Microsoft.RuleEngine;
using System.IO;
using System.Data;
using System.Data.SqlClient;
namespace MyBizTalkApplication.FactRetriever
{
   public class myFactRetriever:IFactRetriever
   {
      public object UpdateFacts(RuleSetInfo rulesetInfo, Microsoft.RuleEngine.RuleEngine engine, object factsHandleIn)
      {
         object factsHandleOut;

         {
            string strCmd = "Initial Catalog=Northwind;Data Source=(local);Integrated Security=SSPI;";
            SqlConnection conn = new SqlConnection(strCmd);
            DataConnection dc = new DataConnection("Northwind", "CustInfo", conn);

            engine.Assert(dc);
            factsHandleOut = dc;
         }
         return factsHandleOut;
      }
   }
}

Note that DataConnections should always be reasserted, when provided from a fact retriever, as shown in the preceding code sample. The engine instance uses the DataConnection to query the database based on the rule conditions, and any rows returned by the query would be asserted into the engine's working memory as TypedDataRows. Reasserting the DataConnection ensures that rows from a previous execution of the engine are cleared from memory.

In fact, there is little advantage to asserting a DataConnection through a fact retriever except that it provides a way to externalize the data source.