Bonus chatter: Getting the Web Forms for Marketers connection string

I hit an issue recently where it was necessary to get direct access to the Web Forms for Marketers database. The code needed to do some custom processing of previous form submissions in a Sitecore website, and it needed to process so much data that the public APIs for WFfM were too slow to use. Slightly annoyingly, the connection string for WFfM is not added to the the standard .Net <connectionStrings/> config element, but is stored in the custom provider configuration in WFfM’s forms.config file:

<formsDataProvider type="Sitecore.Forms.Data.DataProviders.WFMDataProvider, Sitecore.Forms.Core">
  <param desc="connection string">user id=usr;password=pwd; Data Source=localhost;Database=WebFormsForMarketers</param>
</formsDataProvider>

Not ideal. This is compounded by the fact that the data provider that is loaded via this configuration does not expose the connection string either. So there’s no easy way to extract it via the provider model.

There are two ways to tackle this. The first that springs to mind is that since the forms.config file is just XML, it wouldn’t be hard to write some code that loads and parses it to get the data. The alternative is to use a bit of Reflection on the WFfM provider objects, as that lets us get access to non-public data inside the provider classes.

Intuitively, the XML solution sounds like it’ll make heavier use of CPU and memory to me. It’s a fairly big config file to load just to parse out a single string. So I decided to give the Reflection approach a try and see how that worked out.

NOTE: Whenever you write code that uses Reflection to get at things that are not part of the public APIs of someone else’s code you’re taking  a risk. Inherently this approach makes your code more fragile. There’s no guarantee that the internals you’re accessing will still be there in the next release – and that will lead to errors. Also since there’s a provider model here there’s no guarantee that the internals you’re trying to access will be inside any concrete classes that are configured. Reflection is a useful tool, but don’t forget the risk you’re taking. Please remember that if you choose to borrow from this code.

And with that public service announcement out of the way, lets get to some code…
(As usual, you’d want more error handling in production code)

The public entry point for accessing data from Web Forms for Markerters APIs is the Sitecore.Forms.Data.DataManager class. This is a static class which provides a facade on top of the internal logic for finding the real forms data API objects. When it’s initialised, this class creates an instance of the protected Sitecore.Forms.Data.DataProvider class. And in turn, that type uses Sitecore’s configuration factory classes to get an instance of the concrete class for accessing the WFfM data.

So the first task is to get access to this DataProvider class. There are two ways to go here: Use reflection to create an instance of it, or use Reflection to extract the instance created by Sitecore itself. I decided to go with the second one.

var managerType = typeof(Sitecore.Forms.Data.DataManager);
var managerField = managerType.GetField("provider", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
var provider = managerField.GetValue(managerType) as Sitecore.Forms.Data.DataProvider;

The code gets the System.Type for the manager class, then uses that type to extract a reference to the private static field “provider”, which holds a reference to the object we want. Note that since this is a static type, you call GetValue() on the type object itself, rather than on an instance of the type.

Now, looking at the innards of that DataProvider class, it is a bit more helpful. Once it’s loaded the concrete data provider class from configuration, it exposes it in a public property called “InnerProvider”. So we can take that and cast it to its real type:

var innerProvider = provider.InnerProvider as Sitecore.Forms.Data.DataProviders.WFMDataProvider;

Now note that this is one of the places where this could all go to pot in real life. What if the instance of WFfM you run this code against is using the Oracle data provider, or some other provider type? Well the answer is probably a big messy crash because the cast above will return null. So it makes sense to check if you got the right type back or not. If the cast above returns null then you know this instance of WFfM isn’t configured the way you expect. What you choose to do about that is dependent on what you need this code to do – you could fail gracefully, throw an “unexpected configuration” exception or use an alternative path through the rest of this code that knows about other providers.

But for the sake of blog post simplicity, lets assume that cast worked…

The internals of the WFMDataProvider class include a private field called “shortConnection” which stores the connection string value extracted from the WFfM configuration. So that’s what we need to try and extract:

var innerProviderType = innerProvider.GetType();
var field = innerProviderType.GetField("shortConnection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
string connectionString = field.GetValue(innerProvider) as string;

And magically, now we have a connection string that can be used with standard SQL connections.

As an aside, having written the code above it turns out that if you want to take a more supportable approach to having access to this connection data and you are able to change the forms.config setup slightly, then WFfM can be configured to get its connection string from the element.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s