If you're doing cross-reference lookups in a BizTalk map you have a few design options (see BizTalk Pattern: Mapping Reference Data), and the BizTalk native XRef lookup can provide lookup caching (see BizTalk XRef Undocumented feature) - and you'll need caching if your maps runs against large messages.
I prefer not to use the native XRef option because the values are hidden away in the BizTalk schema, are cumbersome to load, and are only available to BizTalk maps. A custom cross-reference implementation is fairly trivial, can be made available to any .NET consumer, and you can control what gets cached and how long the cache lives.
Assuming you have the lookup code in a helper class separate from the map, BizTalk will create a new instance of the helper class for every run of the map. To get the benefit of caching across different calls to the map, you can use a static dictionary of lookup values (see Implement Caching for your BizTalk applications using "static" classes and methods).
The problem with using a static dictionary is that it lives for the duration of the app domain – so the lookup values will be used for every run of the map until the BizTalk host instance is restarted. This is also true for the native Xref cache. Removing stale cache items becomes a manual process, liable to be forgotten.
In a custom implementation you can automate flushing the cache. Monitoring the data source and flushing when it changes is an expensive option, but a cheap alternative is to store the expected lifespan of the cache in configuration, so when the cache reaches the configured age it flushes itself:
<cache key="StatusCodeLookup" lifespan="P1D"/>
<cache key="CustomerLookup" lifespan="PT10M"/>
Returning the lookup value then falls into three parts – flush the cache if expired, check the cache, get from source and add to cache if not already present:
public string GetStatusCode(string inputCode)
private static void EnsureStatusCode(string inputCode)
string outputCode = GetLookupValue(inputCode);
_statusCodes[inputCode] = outputCode;
private static void FlushCacheIfExpired()
TimeSpan lifespan = MapConfiguration.Current.Caching.GetLifespan(CacheConfigKey.StatusCodeLookup);
DateTime expiry = _lastCacheLoad.Add(lifespan);
if (DateTime.Now > expiry)
Full sample on github here: ConfigurableCrossReferenceCache. (This snippet omits logging and error handling for brevity, but in the sample there's plenty of both, which makes building and monitoring your cache much easier).
Using BTSNTSvc.exe.config for the configuration means that if you need to change the lifespan of a cache item you'll need to restart host instances, but if that's an issue you can use Enterprise Single Sign-On as your configuration store, and lookup the value on every access.