Monday, April 28, 2008

HelloSpring -- A Spring.NET Web Part

So, the other day I got asked to try embedding some existing functionality in a web part to use in SharePoint.  The existing functionality was based on Spring.NET and NHibernate and provided services for the misty, mystical world of ORM database access.

Turns out, there is nothing mystical about doing this -- although it took a while to iron out all the wrinkles -- but I thought I'd give a few helpful hints on getting things set up ok.

The quick and dirty cheat-sheet:

  1. Sign all DLLs that will be used by your web part, including Spring.NET DLLs if they aren't already.
  2. Copy the DLLs used by your web part to the correct /bin directory for the SharePoint web application.
  3. Locate the appropriate Spring.NET configuration resources (file based or included in a DLL).
  4. Let the SharePoint web application know about Spring.NET by editing its web.config to point to the configurations it should be interested in.
  5. Set the SharePoint web application trust level to Full.
  6. Write your web part using the same DLLs from step 1 & 2.  Include code to retrieve the Spring.NET ApplicationContext and use to to find the objects you need.
  7. Deploy your web part.

John Hancocks Everywhere

Repeat after me:  everything must be signed.  Everything must be signed.

SharePoint web parts are typically deployed to the GAC and as such must have strong naming in operation.  One of the requirements of a signed assembly is that all the assemblies it reference must also be signed. 

In particular, the Spring.NET assemblies need to be signed.  With Spring.NET 1.1.1, the release assemblies are signed but the debug assemblies aren't.  The source code download does not include the signing settings in the projects.  So, you can either use the release libraries provided in the default /bin directory of the distribution, or you can go into Visual Studio and add signing to all the projects and rebuild (in which case the new DLLs will be in the /build directory).

Nothing is special about Spring.NET here -- if you are using any other DLL, it, too, will need signing.  So, if instead of using Spring.NET directly, you want to use a DLL that in its turn uses Spring.NET, that DLL will need to be signed as well.  If you are using libraries that were built for a straight-forward ASP.NET application, they probably won't be signed right off the bat either.

Sign'em and move on.  See How to: Sign an Assembly if you need a boost on this.

A Stupid Web Part Described

To demonstrate the integration, we're going to create a web part that will write a greeting on a SharePoint page base on a web part property, State.  Different greetings will be issued for Wisconsin, Minnesota, any other non-blank state, and a blank state property.

Wisconsin:
image

Minnesota:
image

Iowa:
image

Empty
image

A Stupid Spring.NET DLL

To implement this, I create a dead-simple Sprint.NET cloud of classes and configuration wrapped up in a DLL.  There is one interface and two classes:

using System;

namespace HelloSpringSpringPart
{
public interface IHelloSpring
{
String Message { get; set; }
}

public class HelloWisconsin : IHelloSpring
{
public String Message { get; set; }
public HelloWisconsin()
{
}
}

public class HelloMinnesota : IHelloSpring
{
public String Message { get; set; }
public HelloMinnesota()
{
}
}
}


Both of these classes are really identical (I said it was stupid), but if you need, you can pretend in your mind they do something more fun -- maybe adding an ena, eh, or uff-da to the message in the property setter.



The configuration file, HelloSpring.xml, is equally simple:






<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
<object id="Wisconsin"
type="HelloSpringSpringPart.HelloWisconsin, HelloSpringSpringPart">
<property name="Message" value="Guten Tag Wisconsin!"/>
</object>
<object id="Minnesota"
type="HelloSpringSpringPart.HelloMinnesota, HelloSpringSpringPart">
<property name="Message" value="Hello Minnesota!"/>
</object>
</objects>



Notice we are inserting the value for Message as part of the object definition.  Make sure the project is signed and build the DLL.



A Stupid Web Part Implemented



In the web part code itself, we need to accomplish two things:




  1. Provide the State property.


  2. Do the right thing when it comes time to put the web part on the page.



Since we're going to use the stupid DLL and Spring.NET, we add references to HelloSpringSpringPart (the above DLL) and Spring.Core.



The property is the normal over-attributed block:



[Personalizable(PersonalizationScope.Shared),
WebBrowsable(true),
WebDisplayName("State Name"),
WebDescription("State to greet"),
Category("Configuration")]
public string State { get; set; }



CreateChildControls gets the Spring.NET context.  This particular version of GetContext pulls its information from the information specified starting in web.config.  Se the next section for that setup.  It also checks the value of the State property and adds a Literal control with appropriate text.  The only mildly interesting section is where we check to see if a Spring.NET object exists in the context before retrieving it and getting its message:



protected override void CreateChildControls()
{
IApplicationContext applicationContext =
Spring.Context.Support.ContextRegistry.GetContext();

Literal stuff = new Literal();
if (string.IsNullOrEmpty(State))
{
stuff.Text = "State is empty";
}
else if (applicationContext.ContainsObject(State))
{
IHelloSpring helloSpring =
(IHelloSpring)applicationContext.GetObject(State);
stuff.Text = helloSpring.Message + "<br/>";
}
else
{
stuff.Text = "No special greeting for " + State;
}

Controls.Add(stuff);
}






Deploy, but do not yet activate the web part.



Wiring it up



Finally, we need to hook all the pieces together.  We do this editing the web.config file for the SharePoint web application we are going to place the web part on.  For the default application, it would be something along the lines of




C:\Inetpub\wwwroot\wss\VirtualDirectories\80\web.config




First we have to tell the web app that in addition to SharePoint and all those other goodies, we're going to include a spring configuration block.




<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
</sectionGroup>
<sectionGroup name="SharePoint">
<section name="SafeControls" ... />
< ... >
</sectionGroup>
<sectionGroup name="System.Workflow.ComponentModel.WorkflowCompiler" ...>
< ... >
</sectionGroup>
</configSections>
<spring>
<context>
<resource
uri="assembly://HelloSpringSpringPart/HelloSpringSpringPart/HelloSpring.xml" />
</context>
</spring>



In the spring configuration section, we are simply telling Spring.NET to pull its default context from the HelloSpring.xml resource in HelloSpringSpringPart.dll.



Further down the config file, we make sure that the web application is set to full trust.




<trust level="Full" originUrl="" />



I have not found a way to maintain a trust level of WSS_Minimal and still get this to work.  Spring.Core uses reflection extensively and that requires the elevated trust level.  I would be happy to learn of a way to get around this.



Next, make sure that all DLLs other than the web part DLL get copied to the /bin directory for the web application.



Finally, activate the web part for the site collection.  Once this is done you should be able to add the part to a web parts page and fiddle with the State property to get the different messages.



Musings



The question, thus, is not "Can I use Spring.NET in a web part" but "Should I use Spring.NET in a web part."  I don't have an opinion yet on that.  Having to set the trust level to Full isn't optimal -- but there are certainly lots of sites that do that when component changes are restricted to trusted staff. 



The loading of the context information could be handle external to the web.config file for SharePoint in order to not be changing it by hand, although the trust level would still need to be done there.



Finally, it would probably be worth tracking down what the behavior of Spring.NET is when there multiple web parts using it on a page.



And that, folks, is how this social butterfly spent his Saturday night.

No comments: