Create Custom SharePoint Web Service WSDL and Disco ASPX Files Automatically

I just completed a SharePoint developer tool called SPDev.exe. The initial purpose of this utility is to auto-generate the web service WSDL.aspx and Disco.aspx files needed for a custom SharePoint web service.

icon_shout Automatically generate your SharePoint disco.aspx and wsdl.aspx files in seconds!
icon_shout No need to manually copy or deploy your .asmx first!
icon_shout No manual editing!
icon_shout No Disco.exe needed!
icon_shout Automatically recycles the application pool (ensuring the newest assembly is loaded)!
icon_shout Automatically deploys the final SharePoint web service files (using the -deploy option)!
icon_shout No UI to enter data each time.  A command tool you can script for fast, consistent re-use!

After having to manually modify the disco.aspx and wsdl.aspx files by hand for the last three years, I finally found it tedious enough to create this utility.  There are other tools out there, but they require you to enter information into a Windows UI, they are not script-able, and still felt to tedious for me.  Call me lazy.  :)  Time is money.  This tool is a command line utility.  Simply execute the SPDev.exe command and your MyServiceDisco.aspx and MyServiceWsdl.aspx files are immediately ready for deployment.  SPDev also allows you to script the process.  Create a .cmd or .bat file and you’ll never have to enter any parameters again.  Another big benefit is you don’t need to use the Disco.exe utility since SPDev uses IIS and .NET instead which will always be on your development machine – since you are developing for SharePoint.  And, there are no temp files are generated (unless you want them generated).

Documentation for this utility is on my blog here: http://blog.crsw.com/spdev/

SharePoint RunWithElevatedPrivileges Example

The following example sends an email using elevated privileges.  This will elevate the privileges to execute using the service account.

The SPSecurity.RunWithElevatedPrivileges method taks a delegate method as its argument and executes that code with the service account.

SPSecurity.RunWithElevatedPrivileges( 
    delegate()
    {
        // Your code needing elevated permissions goes here.
    }
)

Example:

public void SendEmail(Email email) 
{ 
   SPSecurity.RunWithElevatedPrivileges(delegate() 
   { 
        SmtpClient mail = new SmtpClient();    MailMessage message = new MailMessage(); 
        message.From = new MailAddress(this.SenderAddress); 
        message.To.Add(email.To); 
        message.IsBodyHtml = email.IsHtml; 
                    
        if (!string.IsNullOrEmpty(email.Cc))            
            message.CC.Add(email.Cc); 
                    
        if (!string.IsNullOrEmpty(email.Bcc))            
            message.Bcc.Add(email.Bcc); 
                    
        if (!string.IsNullOrEmpty(this.ReplyToAddress))             
            message.ReplyTo = new MailAddress(this.ReplyToAddress); 
                    
        if (!string.IsNullOrEmpty(email.Subject))            
            message.Subject = email.Subject; 
                    
        if (!string.IsNullOrEmpty(email.Body))            
            message.Body = email.Body; 

        mail.Host = this.CentralAdminOutboundServerAddress; mail.Send(message); 
    });
}

How to Create a Custom List with a Custom Content Type and Views

 

Steps to creating a new list with custom views and a custom content type:

Why use a custom content type?

  1. Create your site columns (fields) and content type, and deploy it to your development farm.
  2. Create a new generic Custom list on any site of your choosing.  Enable content type management on the list.
  3. Remove the default Item content type from the list.
  4. Add your new custom content type to the list, it should be the default.
  5. Customize your views as needed.
  6. Use the SharePoint Solution Generator to generate a List Definition for your new list.  The resulting generated Visual Studio project will contain a folder for the list that contains several form files (DispForm.aspx, EditForm.aspx, and NewForm.aspx) and an .aspx file for each view you created such as AllItems.aspx.  You will also have a schema.xml file which contains the content type definition, the views definition, and the fields section.  Leave the Fields and Views nodes/sections alone as they contain the definitions you defined.
  7. In the schema.xml file, in the ContentTypes node you will find a ContentType node defining your custom content type.  Comment (or delete) this entire node.  We will add this content type to the list via code instead of defining its association here declaratively.

The Major Task content type is commented out here because we are going to add
it programmatically via the ListInstances feature receiver.  This allows us to
properly promote all the fields in the content type to the list and enable
content type management on the list much more easily.

Logging to the SharePoint Log Files

Thanks to Brad Younge for sharing this a while back.  Below is an example on how to write to the SharePoint log files (located in C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\LOGS folder).

catch(Exception e) 
{
   Microsoft.Office.Server.Diagnostics.PortalLog.LogString("Exception: {0} - {1}", e.Message, e.StackTrace);
}

You’ll likely want to prefix your log entries with a date and time stamp.

I’m mentioned (twice) in an MSDN Magazine article – SWEET!

It is an exciting moment to have two articles on my blog discussed and referenced in the May 2008 edition of MSDN Magazine.  Thanks to Ethan Wilansky for a great article in MSDN Magazine, and for making this happen!

MSDN-Mag-May2008CoverHere is the link to the MSDN Magazine article:
MOSS 2007: Automate Web App Deployment with the SharePoint API

Here are the links to two of my blog articles that are referenced in this MSDN Magazine article:
How To: Create a SharePoint Solution for an InfoPath Form 
How To: Modify the web.config file in SharePoint using SPWebConfigModification

Also, Shawn Feldman, a coworker, was mentioned for his terrific work in deploying SQL Reports.

How To: Modify the web.config file in SharePoint using SPWebConfigModification

by Mark Wagner

Summary
 
SharePoint 2007 (WSS and MOSS) allows you to easily and safely add and remove modifications to the web.config file. This post briefly covers what the SPWebConfigModification class can do for you, and how to best to use it.

I hope to improve this post with additions, corrections, and contributions from the public, so please provide any constructive comments and suggestions you have.

Technologies this applies to:

  • Windows SharePoint Services 3.0 (WSS 3.0)
  • Microsoft Office SharePoint Server 2007 (MOSS 2007)

Acknowledgements

While I was scavenging for any information I could find regarding the SPWebConfigModification, there were a few posts I found useful. Information seemed to be scarce and I ended up learning most through trial and error; however, there a couple good nuggets and others I must achnowledge.

  • Shawn Feldman is a coworker (very sharp developer) who was the first to make me aware of the SPWebConfigModification class. Additionally, he provided me with the original code samples, some of which I have since modified and included in my WebConfigurator helper class (a separate post to come).
  • Vincent Rothwell has a post entry that provides, in my opinion, a “Best Practice” on how to best remove your modifications using SPWebConfigModification. It’s simple and right on target.
  • Antonz has a post entry that addresses the proper way to create a section that can be removed.
  • Daniel Larson also has a few a posts that provide a basic overview and examples on how to use SPWebConfigModification.

Overview of SPWebConfigModification

Through the use of the SPWebConfigModification class and xpath you can safely add and remove modifications to the web.config file. There are a number of benefits to using SPWebConfigModificaton to modify the web.config.

SharePoint provides access to a SharePoint web application via the object model using the SPWebApplication class. The SPWebApplication class has a SPWebConfigModifications collection property that contains all the modifications (SPWebConfigModification objects) made to this web application’s web.config file. This is also where we can add new modifications and have them applied to the web.config – or remove them. Additionally, you can iterate through this collection and inspect what changes have been applied. This contains all changes – including yours and those made by other assemblies. You can determine (or attempt to determine) where each change was made by inspecting the owner property.

Every modification has an Owner. Every change made the web.config can be assigned an owner. The owner is nothing more than a string value that uniquely identifies your changes from any other changes that have been made, or may be made in the future.  I suggest using the name of the assembly, or the feature name or feature ID.

Removing is not merely changing a value. By assigning an owner to each new change you make, you can very easily remove your changes. In the case where your original modification was an addition, where you added a new node or attribute, your changes are simply deleted and remove from the web.config file when they are removed. In the case where your modification changed the value of an existing node, upon removing your modification SPWebConfigModification automatically assigns the appropriate previous value. For example, let’s say we wanted to change the Trust level in the web.config to “Full” from whatever it currently is. The Trust level is usually set to a value of “WSS_Medium” by default, but it could easily be some other value before we make our changes. The great thing is that we don’t need worry about what the current value is if ever we need to remove our modification. We do this by removing our change, not by trying to change the value back to a predetermined value such as “WSS_Medium” – SPWebConfigModification manages this for us. This is a big advantage.

Propagate your web.config changes across the farm. SharePoint is designed to be very scalable and easily scalable. The changes you make to the web.config will need to be made to every front-end web server in the farm. SPWebConfigModification can manage this for you very easily.

Also, features are likely the method you will want to use to apply your changes. Using a feature (that can be either hidden or visible) will ensure your web.config changes are applied if a new web server is added to the farm, or rebuilt from scratch. Applying your web.config changes via a feature will ensure your changes are applied to new or newly built web servers – without having to refer to documentation or manually apply your changes to the web.config file on each and every web server.

SPWebConfigModification properties

SPWebConfigModification on MSDN

Name
Gets or sets the name of the attribute or section node to be modified or created.  This is an XPath expression relative to the parent node (specified by the Path property). Sometimes this is nothing more than the name of the new or existing node you want to change. For example: “pages”.

However, this often times needs to contain an XPath expression that is more spcific in order to properly specify the correct node to modify (if it exists) or to create.  For example: “add[@assembly=’myAssemblyName’]”

Owner
Gets or sets the owner of the web.config modification. This should to be a unique string value that will allow you to uniquely identify your modifications from any other modifications made to the web.config.  Suggestions are to use the assembly name, feature name, or feature ID.  This will ensure you are able to easily and cleanly remove your modification when the need arises.

Path
Gets or sets the XPath expression that is used to locate the node that is being modified or created. This value usually contains simple XPath expression to the parent node of the item you intend to change or create. For example: “system.web/httpHandlers” or “system.web/customErrors”.

This can also be a more specific XPath expression to select (or create) a node with a specific attribute value. For example:
“configuration/configSections/sectionGroup[@name=’mySection’]”

Sequence
Gets or sets the sequence number the modification. I have not performed any detailed testing with this parameter. I have always just set this value to zero (0) with no problems.

Type
Gets or sets the type of modification for this object instance. The three values available are defined via the SPWebConfigModification.SPWebConfigModificationType and are EnsureChildNode, EnsureAttribute, and EnsureSection. Caution: Use the EnsureSection type with prudence as nodes created with EnsureSection cannot be removed. See Best Practice section below.

UpgradedPersistedProperties
Gets the collection of field names and values for fields that were deleted or changed. I have not investigated this property and do not know of any need for using it. (inherited from SPAutoSerializingObject)

Value
Gets or sets the value of the item to set. This is usually the complete xml element tag or attribute value for the node specified in the Name property (or as the name parameter in the constructor).

How to use SPWebConfigModification

Using SPWebConfigModification is very easy, which makes it even better.

Below is a quick example of code that changes the mode attribute to a value of “Off”. Here is a sample of what the web.config node might look like that this code would be modifying.

<configuration>
   <system.web>
      <customErrors mode="On">
   <system.web>
<configuration>

Here is the quick example that will change the mode attribute to “Off”. Comments are inline describing each section of code.

private void SimpleSample()
{
    // Get an instance of my local web application
    SPWebApplication webApp = new SPSite("http://localhost").WebApplication;

    // Create my new modification to set the mode attibute to "Off".
    // Example: <customErrors mode="Off">
    SPWebConfigModification modification = new SPWebConfigModification("mode", "system.web/customErrors");
    modification.Owner = "SimpleSampleUniqueOwnerValue";
    modification.Sequence = 0;
    modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute;
    modification.Value = "Off";
    // Add my new web.config modification.
    webApp.WebConfigModifications.Add(modification);
    // Save web.config changes. 
    webApp.Update();
    // Applies the list of web.config modifications to all Web applications in this Web service across the farm.
   
webApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
}

More examples can be found later in the article.  Additonally, I will be posting my WebConfigurator helper class that contains a number of helper methods to centralize and simplify commonly modified elements in the web.config file.

Best Practices

I found it difficult to fully understand exactly how to use SPWebConfigModification as well as how it should and should not be used, and how I should expect it for function. For this reason I have started a list of best practices when using SPWebConfigModification.

Saving your modifications

While first using the SPWebConfigModification class, everything was going fairly smooth with testing and deployment – as it usually does on the developer machine. Of course my development machine is a single farm configuration, which is standard. Executing my web.config modifications on my single server worked just fine. However, when I deployed my code to two other farm configurations, the changes were not being applied to the web.config on any web server. Searching on the Internet surfaced a few other comments posted by others who were experiencing the same problem.

So, to correctly apply your changes on any farm configuration, be sure to perform the following two steps in your code:

  1. Use the ApplyWebConfigModifications() method on the SPWebApplication object you are saving your SPWebConfigModification object to. I don’t understand why this works over the other way, but it solved the problem for me. Example:
    myWebApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();

    Note: This code did not work across the farm (I could not tell you why).

    SPFarm.Local.Services.GetValue< SPWebService>().ApplyWebConfigModifications();
  2. Execute the Update() method on the SPWebApplication object. This will serialize the web application state and propagate changes across the farm – including your web.config changes.
    myWebApp.Update();

So, whenever you want to save your modification to the web.config file, you should execute the following two statements.

// Save web.config changes.
myWebApp.Update();

// Applies the list of web.config modifications to all Web applications in this Web service across the farm.
myWebApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications(); 

Removing your modifications

You should remove your modifications by checking the owner property on each SPWebConfigModification object in the SPWebConfigModifications collection. There are many examples that demonstrate removing modifications by creating a new SPWebConfigModification object and then removing that object. A must simpler, cleaner, and safer method is to remove your original SPWebConfigModification object by checking the Owner property on the SPWebConfigModification object.  Credit goes to Vincent Rothwell for sharing this logic.  Here is an example:

public void RemoveConfiguration(string owner)
{
   if (myWebApp != null)
   {
      Collection<SPWebConfigModification> collection = myWebApp.WebConfigModifications;
      int iStartCount = collection.Count;
      // Remove any modifications that were originally created by the owner.
      for (int c = iStartCount - 1; c >= 0; c--)
      {
         SPWebConfigModification configMod = collection[c];
         if (configMod.Owner == owner)
            collection.Remove(configMod);
      }
      // Apply changes only if any items were removed.
      if (iStartCount > collection.Count)
      {
         myWebApp.Update();
         myWebApp.Farm.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
      }
   }
}

Creating Sections
(EnsureSection vs. EnsureChildNode)

When creating new section you should use the EnsureChildNode modification type value instead of the EnsureSection. This will allow you to remove your custom section when the need arises. Antonz also discovered this as well and has an earlier post worth mentioning. Here is an example of the two options, both creating a section node like this: <mySection></mySection>

This cannot be removed (using EnsureSection):

SPWebConfigModification mod = new SPWebConfigModification("mySection", "configuration");
mod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureSection;

This can be removed (using EnsureChildNode):

SPWebConfigModification mod = new SPWebConfigModification("mySection", "configuration");
mod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
mod.Value = "<mySection />";

Save Only Once per Event or Feature Activation

It is important to remember that when developing and testing on a single server installation of SharePoint there are many activities that are able to complete instantly.  However, when that same section of code executes on a SharePoint farm with multiple servers the time it takes to complete a given task is longer.  This is due to timer jobs and farm synchronization events that must occur.

For this reason, you should only execute the ApplyWebConfigModifications() method only once within a feature activation or event handler.  If more than one call to the ApplyWebConfigModifications() method is made in close succession you will likely get the following error:

A web configuration modification operation is already running.

This is because request to save your changes from the first call to the ApplyWebConfigModifications() is not fully completed across the farm.

SPWebConfigModification Sample Feature

I intend to post a blog entry containing code samples of my WebConfigurator class and a sample of how to implement SPWebConfigModification using a web application feature.

Related Posts

WebConfigurator – a helper class to assist in using SPWebConfiguration.

References

Create an IE Favorite to quickly resize your browser

Often times when editing or designing a web page or a site I use my full screen to take advantage of all the real estate my screen has to offer.  I also often times need to quickly see what a page will look like in a smaller screen resolution such as 1024×768.  Here is a quick tip to quickly and easily resize your IE browser to a specific size.
  1. Create a shortcut in your IE Favorites or in you Links folder.
  2. Rename this shortcut/favorite to “Resize Window to 1024 x 768″.
  3. Edit the properties of the shortcut and place the following line of javascript in the URL property.

    javascript:window.resizeTo(1024,768)

  4. Note: You may receive a warning prompt. Simply proceed.
  5. Save the properties and then click your new favorite “Resize Window to 1024×768″.

Your current window should now automatically resize to 1024 x 768.  I have a shortcut/favorite for both 800 x 600, 1024 x 768, and 1152 x 864 so I can quickly and easily resize my browser to a specific resolution.

Other common lower resolution sizes are:

javascript:window.resizeTo(800,600)
javascript:window.resizeTo(1024,768)
javascript:window.resizeTo(1280,720)
javascript:window.resizeTo(1280,800)

Important: When using IE 8 or later (using tabs), this will only work when you have only one tab in your IE browser. This will not work when you have multiple tabs open in the same IE window.

Convert a string to an enumerated (enum) value.

Using the Enum.Parse method, you can easily convert a string value to an enumerated value.  Doing this requires the type of the enum and string value.  Adding the true argument will cause the case to be ignored.

Using the following enum for this example:

private enum Aircraft 
{
   Beech,
   Cessna,
   Piper
}

You can easily convert the string to an enum value like this:

Aircraft air = (Aircraft) Enum.Parse(typeof(Aircraft), "Cessna", true); 

Ideally you should wrap a try-catch around the Enum.Parse statement.

Simple String Date Validation

Simple string date validation.

Updated: Use DateTime.TryParse() method instead (which was introduced after this original posting which was originally posted during the .NET version 1.x days and moved from my old blog)

Example:

DateTime testDate; 
bool isValidString = DateTime.TryParse(myDateString, out testDate);

Original (Obsolete) Posting during .NET 1.0:
I am a big fan of maintaining a library of simple and clean helper methods.  Here is a simple and clean way to verify if a string formatted date is a valid date.  This allows you to encapsulate the exception handling making it easy to use and very readable – another important coding practice.

private static bool IsDate(string sDate) 
{ 
    DateTime dt; 
    bool isDate = true;

    try
    {
        dt = DateTime.Parse(sDate); 
    }
    catch 
    { 
        isDate = false; 
    }     return isDate; 
}

Trace.axd – Application Trace

The more you learn, the less you know.

Trying to keep up with the extensive areas of .NET and Microsoft products can easily bring you to this conclusion.  Its easy for us to continually focus on all the new technologies and versions (Whidbey and the .NET Framework 2.0) only to overlook some of the low hanging fruit – at least for me it is.  Remember, this is the “Simple Thoughts from a Simple Mind“ blog. ;)

One area I forgot about is the ASP.NET Trace – Trace.axd page.  As you may know, ASP.NET allows you to trace the processing for a single page or for all pages.  The trace output can be rendered at the end of each page.  Don’t forget (as I did) there is also the Trace.axd page that provides a nice list of traced pages such as this.

TracePic1 

The web.config file needs only a small change to make this available.

 TracePic2

Clicking on the View Details link for a page process will present you with the familiar trace detail.