Using Reflection in SharePoint

I recently had the opportunity to work with Vlad Catrinescu on one of his CodePlex project. The project in question is the SharePoint Host Names Site Collection Creator or for short HNSC.

Vlad had created, back in September of 2013, a Windows application to create a host named site collection through a GUI instead of running some PowerShell. The idea was interesting, especially when Microsoft recommends to create host named site collection.

A user by the username johnmclavert suggested to have the option inside the Central Admin which, if you ask me, makes a lot of sense. He even pointed to another project that had very similar goals: https://chnsc.codeplex.com/

The only downside with the CHNSC solution was that it wasn’t creating the host named site collection, but generating the PowerShell command to do so.

Wanting to improve both solutions and to contribute to the community, I suggested to Vlad the idea of creating such a solution. Without hesitation, he agreed and this is where my journey started.

The first thing I did was to analyze the Windows application that Vlad built. Right away, I noticed he was actually generating the PowerShell command and executing it by creating a Runspace and passing said command. If you want more information and how this is done, I invite you to read this topic on MSDN.

His code was generating the PowerShell command for creating site collection, which is New-SPSite.

Seeing this, the first question that popped into my head was: Why not do the same thing using the SharePoint object model?

If you are not yet familiar with creating a Site Collection, it is done by calling the Add method on a SPSiteCollection object.

Example:

SPContext.Current.Site.WebApplication.Sites.Add()

If you look on MSDN, under the members of SPSiteCollection, you can see there’re 12 overload for creating a SPSite, but curiously none of them gives you the possibility the create a host named site collection.

So I did what all good developer would do, I took a peak in Microsoft code with a decompiler. I don’t really have a preference between the different .NET decompilers, but I chose to use Telerik JustDecompile.

The first thing I wanted to inspect was the code behind the New-SPSite. If you didn’t already know, all the SharePoint PowerShell Cmdlets can be found in the Microsoft.SharePoint.Powershell.dll. This dll file is found in the GAC.

Upon decompiling the dll, it’s just a matter of finding the class. In this case, it’s called SPCmdletNewSite.

spcmdletnewsite

 

We then look for the method that creates the SPSite.We can find it quite easily; because by looking at each method signature, it’s the one with the return value of type SPSite. Only one method returns a SPSite, that’s the CreateDataObject().

createdataobject

 

In the method, there is some validation and other stuff; but the interesting part was the following line:

SPSite mAdministrationSiteType = this.webApp.Sites.Add(this.contentDb, 
        this.m_SpecifiedSiteSubscription,this.Url, this.Name, this.Description,
        this.Language, num, name, str1, str1, this.OwnerEmail, str2, str2,
        this.SecondaryEmail, this.m_StringQuota, null, this.useHostHeaderAsSiteName,
        this.OverrideCompatibilityRestriction);

That’s an overload of the SPSiteCollection.Add() method I talked about earlier, but it wasn’t one of the 12 overload methods that MSDN documented. That usually means one thing, it’s not public. Indeed, when you decompile the Microsoft.SharePoint.dll and look at the SPSiteCollection class, you find 5 internal methods, including the one called by the PowerShell New-SPSite.

 

powershell

Usually, if a method is internal, it’s because it is not meant to be used by external code for some reason. However, in this scenario I couldn’t understand why. It’s not like Microsoft doesn’t want you to create host named site collections. It’s even the opposite, they recommend it. Then why do they offer the possibility to create it through PowerShell, but not by the SharePoint object model?

At that point, I discussed with Vlad my findings. I mentioned to him that instead of running some PowerShell commands using Runespace, I could call the internal method that the New-SPSite calls. The only disadvantage that we could come up with was that if Microsoft changes the signature of this method, our solution wouldn’t work anymore.

So what? I’ll just update the code. It’s not like it’s that complicated anyway. Maybe it’s a foolish decision, but I’m willing to cope with it.

So this is what I ended up doing for the HNSC SharePoint farm solution.

Just a small warning before I continue on. Reflection is very powerful and shouldn’t be used for everything. It has a lot of performance overhead and can rapidly lead to problematic scenarios. It has to be used with caution.

That said, the first thing you need to do in order to run an internal method is to get the Type of the calling Object. For us, this is the SPSiteCollection.

SPSiteCollection sites = webApp.Sites; 
Type type = sites.GetType();

Once you have the Type, you are able to get the MethodInfo. It’s also quite straight forward:

MethodInfo methodInfo = type.GetMethod("Add");

Next is to invoke our method, but wait…what about the parameters? Well actually, that’s kind of easy. You create an Array of objects. In my case, it looks like this:

object[] parametersArray = new object[] { database, siteSubscription,
        siteUrl, title, description, nLCID, compatibilityLevel, webTemplate,
        ownerContact.Login, ownerContact.Name, ownerContact.Email, secondaryContact.Login,
        secondaryContact.Name, secondaryContact.Email, quotaTemplate, null, true, false };

As you can see, the last 3 parameters are hard-coded. I’ll explain each one of them in order:

  1. The first one represent the sscRootWebUrl. If you look at the code CreateDataObject() method I wrote about earlier, even the New-SPSite passes null directly. So I did the same.
  2. This is actually the Boolean that determines if we are creating a host named site collection or not. It’s named useHostHeaderAsSiteName. It’s obvious that I have to put true here.
  3. The last parameter is the overrideCompatibilityRestriction. It’s an optional parameter in the New-SPSite Powershell command and by default it’s false.

After doing all of this, we get to the last step, which is to run our internal method. Since we know that this method returns a SPSite, we cast the Invoke method.

SPSite newSite = (SPSite)methodInfo.Invoke(sites, parametersArray);

Thinking I was done, I ran the code just to find the following error:

System.Reflection.AmbiguousMatchException: Ambiguous match found.

So what’s happening? Reflection cannot find the method, it actually finds more than one, even when passing the Array of object as parameters. So how do we fix this? Well there is several overloads to call the GetMethod() method. The one that we are interested in is the overload where you can specify the exact signature of the method you are looking for.

The first thing we need is a new array of Type. It looks something like this:

Type[] signature = new Type[] { typeof(SPContentDatabase), typeof(SPSiteSubscription), typeof(string),
        typeof(string), typeof(string), typeof(uint), typeof(int), typeof(string), typeof(string),
        typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string),
        typeof(string), typeof(bool), typeof(bool) };

After it’s a question of passing this array to the GetMethod().

Something else to keep in mind is that we are dealing with an internal method. So we need to specify the appropriate BindingFlags:

MethodInfo methodInfo = type.GetMethod("Add", 
        BindingFlags.NonPublic | BindingFlags.Instance,
        null, signature, null);

That’s it! The code now creates a host named site collection using the same method used by the PowerShell New-SPsite.

Here’s the complete code:

SPSiteCollection sites = webApp.Sites;

Type type = sites.GetType();

Type[] signature = new Type[] { typeof(SPContentDatabase), typeof(SPSiteSubscription), typeof(string),
        typeof(string), typeof(string), typeof(uint), typeof(int), typeof(string), typeof(string),
        typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string),
        typeof(string), typeof(bool), typeof(bool) };

MethodInfo methodInfo = type.GetMethod("Add", 
        BindingFlags.NonPublic | BindingFlags.Instance,
        null, signature, null);

object[] parametersArray = new object[] { database, siteSubscription,
        siteUrl, title, description, nLCID, compatibilityLevel, webTemplate,
        ownerContact.Login, ownerContact.Name, ownerContact.Email, secondaryContact.Login,
        secondaryContact.Name, secondaryContact.Email, quotaTemplate, null, true, false };

SPSite newSite = (SPSite)methodInfo.Invoke(sites, parametersArray);

Now let’s just hope the signature of this internal method doesn’t change in the next CU and remember to take a look at the complete solution: https://hnsc.codeplex.com

Thanks!

About the author 

Joseph Henry Passineau