Kanithi's Professional Blog

September 1, 2014

Custom web.config changes using generic feature receiver class

Filed under: .NET, SharePoint 2013 — Veera Kanithi @ 3:45 pm

While I was looking for a general solution to edit Web.Config using a SharePoint feature, I found an excellent article from Ryan McIntyre about creating an external XML file with all the custom modifications and using a common feature receiver it would take the changes from the external xml file and merge them in web.config using SPWebConfigModification object.

I am impressed with this approach, as this will eliminate frequent changes to the feature receiver class and avoid compilation and rebuilding WSP. Just simply change the XML file and re activate the feature, it’s that simple. Enough said, full details including the feature receiver class is in his site below.

http://randomdust.com/blogs/ryan/archive/2008/03/22/featurereceiver-for-applying-custom-web-config-changes.aspx

But the code Ryan used for feature receiver class, works only for element modifications not for creating new sections. In my case I got to create a new section and in the web.config file like “connectionStrings”. After further exploring, he used EnsureChildNode modification type, this will be good only for modifications, if you want create new sections we must use EnsureSection. But EnsueSection has a disadvantage, we can’t remove the sections from web.config on de activating the feature. But I felt it is fine to leave an empty section in the web.config, it doesn’t harm. Following are my modifications (highlighted) to the feature receiver class that Ryan developed, to accommodate both sections and elements.

protected SPFeatureReceiverProperties _properties;

public override void FeatureActivated(SPFeatureReceiverProperties properties) {

string fileLoc = properties.Definition.RootDirectory + “\\WebConfigChanges.xml”;

//Check to see if a WebConfigChanges.xml file exists. If yes, we have work to do

if (System.IO.File.Exists(fileLoc))

{

//Grab the properties

_properties = properties;

this.ProcessChanges(fileLoc, false);

}

}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {

string fileLoc = properties.Definition.RootDirectory + “\\WebConfigChanges.xml”;

//Check to see if a WebConfigChanges.xml file exists. If yes, we have work to do

if (System.IO.File.Exists(fileLoc))

{                //Grab the properties

_properties = properties;

this.ProcessChanges(fileLoc, true);

}

}

public override void FeatureInstalled(SPFeatureReceiverProperties properties) {

/* no op */

}

public override void FeatureUninstalling(SPFeatureReceiverProperties properties) {

/* no op */

}

private void ProcessChanges(string FileLocation, bool removeModification)

{

string xPathLocation;

string elementName;

string sectionName;

Dictionary<string, string> attributes = new Dictionary<string, string>();

using (XmlReader reader = XmlReader.Create(FileLocation))

{

//Loop through all of the changes

while(reader.ReadToFollowing(“WebConfigChange”))

{

//Clean out any attributes from past iterations

attributes.Clear();

xPathLocation = reader.GetAttribute(“XPathLocation”);

elementName = reader.GetAttribute(“ElementName”);

//Check if there is any new Section required

sectionName = reader.GetAttribute(“SectionName”);

elementName = reader.GetAttribute(“ElementName”);

//Make sure we have at least a path and element

if (xPathLocation == null || elementName == null || sectionName == null)

                throw new Exception(“WebConfigChange missing required XPathLocation or ElementName or SectionName attributes”);

//Get the Attributes to apply

if (reader.ReadToDescendant(“Attribute”))

{

do

{

attributes.Add(reader.GetAttribute(“Name”), reader.GetAttribute(“Value”));

} while (reader.ReadToNextSibling(“Attribute”));

}

//Do the update

UpdateWebConfig(xPathLocation, elementName, sectionName, attributes, removeModification);

}

}

}

private void UpdateWebConfig(string XPathLocation, string ElementName, string SectionName,

  Dictionary<string, string> Attributes, bool removeModification)

{

try

{

SPWebApplication webApp = null;

//Get the web app

//First check if it was deployed to a Site Collection

SPSiteCollection siteCol = _properties.Feature.Parent as SPSiteCollection;

if (siteCol == null)

{

//Check if it was deployed to a site

SPSite site = _properties.Feature.Parent as SPSite;

if (site == null)

{

//Check if it was deployed to a Site

SPWeb web = _properties.Feature.Parent as SPWeb;

if (web != null)

webApp = web.Site.WebApplication;

}

else

webApp = SPWebApplication.Lookup(new Uri(site.Url));

}

else

webApp = siteCol.WebApplication;

if (webApp != null)

{

SPWebConfigModification modification;

//If no section name

          if (SectionName == null)

          {

              modification = new SPWebConfigModification(ElementName + CreateAttributeString(Attributes), XPathLocation);

              modification.Owner = “Company.MOSS.FeatureReceiver”;

              modification.Sequence = 0;

              modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;

              modification.Value = string.Format(CultureInfo.InvariantCulture, CreateModificationValueString(ElementName, Attributes), CreateModificationValueArgs(Attributes));                     

          }

          else

          {

              modification = new SPWebConfigModification(SectionName, XPathLocation);

              modification.Owner = ” Company.MOSS.FeatureReceiver”;

              modification.Sequence = 0;

              modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureSection;

              modification.Value = String.Format(“<{0}/>”, SectionName);

          }

           

          if (removeModification)

              webApp.WebConfigModifications.Remove(modification);

          else

              webApp.WebConfigModifications.Add(modification);

webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();

}

else

throw new ApplicationException(“Could not locate a web application”);

}

catch (Exception ex)

{

System.Diagnostics.EventLog el = new System.Diagnostics.EventLog();

el.Source = “WebConfigFeature”;

el.WriteEntry(ex.Message);

}

}

/// <summary>

/// Accepts a dictionary object with all of the attributes for the web modification and

/// creates a string representing the attribute values which can be used when creating

/// the SPWebConfigModification object.

/// </summary>

/// <param></param>

/// <returns></returns>

private string CreateAttributeString(Dictionary<string, string> Attributes)

{

//Create a string that looks like this (no line breaks):

//[@Assembly=\”Company.Moss.Activities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9eed2245513232a4\”]

//[@Namespace=\”Company.Moss.Activities\”]

//[@TypeName=\”*\”][@Authorized=\”True\”]

string result = “”;

//Check if there are attributes

if (Attributes.Count > 0)

{

foreach (KeyValuePair<string, string> kvp in Attributes)

{

result += “[@” + kvp.Key + “=\”” + kvp.Value + “\”]”;

}

}

return result;

}

private string CreateModificationValueString(string ElementName, Dictionary<string, string> Attributes)

{

//Create a string that looks like this:

//”<authorizedType Assembly=\”{0}\” Namespace=\”{1}\” TypeName=\”{2}\” Authorized=\”{3}\”/>”

string result = “<” + ElementName;

//Check if there are attributes (Kind of silly if there aren’t!)

if (Attributes.Count > 0)

{

int i = 0;

foreach (string key in Attributes.Keys)

{

result += ” ” + key + “=\”{” + i.ToString() + “}\””;

i++;

}

}

result += ” />”;

return result;

}

private object[] CreateModificationValueArgs(Dictionary<string, string> Attributes)

{

//Create an object that looks like this:

//”Company.Moss.Activities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9eed2245513232a4″, “Company.Moss.Activities”, “*”, “True”

object[] result = new object[Attributes.Count];

int i = 0;

foreach(string value in Attributes.Values)

{

result[i] = value;

i++;

}

return result;

}

Here is the sample WebConfigChanges.xml file with connectionStrings section and other elements.

<?xml version=”1.0″ encoding=”utf-8″ ?>

<WebConfigChanges>

<WebConfigChange XPathLocation=”configuration” SectionName=”connectionStrings”>

</WebConfigChange>

<WebConfigChange XPathLocation=”configuration/connectionStrings” ElementName=”add”>

<Attributes>

<Attribute Name=”name” Value=”MOSSConnectionString” />

<Attribute Name=”connectionString” Value=”Data Source=XXX;Initial Catalog=XXX;User; Password=XXX” />

<Attribute Name=”providerName” Value=”System.Data.SqlClient” />

</Attributes>

</WebConfigChange>

<WebConfigChange XPathLocation=”configuration/system.web/httpHandlers” ElementName=”add”>

<Attributes>

<Attribute Name=”verb” Value=”*” />

<Attribute Name=”path” Value=”ChartImg.axd” />

<Attribute Name=”type” Value=”System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″ />

</Attributes>

</WebConfigChange>

</WebConfigChanges>

Remember to place this file in the folder where elements.xml of the feature file is located under the 12 hive.

Create a free website or blog at WordPress.com.