Thursday, October 28, 2010

Model View Presenter Pattern with Sharepoint Service Locator

After playing around with the Sharepoint 2010 Guidance Package, I decided to implement a MVP pattern within a WebPart using the ServiceLocator component. Let’s start by creating a new Visual WebPart with the according project template in Visual Studio 2010.

This template is a real improvement and generates all components necessary for a Visual WebPart, especially an ASP.NET user control (ASCX), which can be designed using the visual editor in Visual Studio. The following image shows the project structure after executing the template:

The project contains nearly all elements we need for MVP. The WebPart class (VisualWebPart1) will become our model. Actually it’s more kind of a controller and initializes the view and also provides access to data. Of course, the UserControl will be the view. So all we need additionally is the Presenter and some interfaces. Let’s create the interfaces first. We create one for the WebPart class. In the example the interface provides just one method called GetNames, which returns a List of strings just for testing:
public interface IVisualWebPart1
{
    System.Collections.Generic.IList<string> GetNames();
}

We also need an interface for the View, so that the Presenter can call the appropriate methods on it without knowing the concrete implementation. In the example the View has a Fill method, which accepts a list of strings:
public interface IVisualWebPart1UserControl
{
    void Fill(System.Collections.Generic.IList<string> names);
}

With that interfaces we can now define our Presenter. I will create a generic base class for the Presenter, which can be reused in other projects:
public class Presenter<IView, IModel>
{
    public Presenter()
    {
        IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent();
        Model = serviceLocator.GetInstance<IModel>();
    }

    public Presenter(SPSite site)
    {
        if (site != null)
        {
            IServiceLocator serviceLocator =
SharePointServiceLocator.GetCurrent(site);
            Model = serviceLocator.GetInstance<IModel>();
        }
    }

    protected IModel Model { get; set; }

    public IView View { get; set; }

    public virtual void OnViewInitialized() { }

    public virtual void OnViewLoaded() { }
}

The Presenter is the connection between the Model and the View. It expects to get the Model by using the Sharepoint Service Locator. The View property will be set by the View itself upon its creation. The derived Presenter in the example (VisualWebPart1Presenter) uses IVisualWebPart1 as Model and IVisualWebPart1UserControl as View. It overrides OnViewInitialized and calls the Fill method of the View with the list provided by the Model. So all we need to do know is to create the Presenter in the View and call its OnViewInitialized method. The View gets a property of type VisualWebPart1Presenter:
VisualWebPartMVPPresenter _presenter;
VisualWebPartMVPPresenter Presenter
{
    get
    {
         if (_presenter == null)
         {
            _presenter = new VisualWebPartMVPPresenter(SPContext.Current.Site);
            _presenter.View = this;
        }
        return _presenter;
    }
}

We’re nearly done. The last thing to do is to register the Model with Sharepoint Service Locator. As said in my previous post, I will use a feature receiver to do that. A right click on Feature1.Feature opens the context menu and there you find an option to add an event receiver. Visual Studio creates a class derived from SPFeatureReceiver and also adds the important method overrides which you only need to uncomment. Within the FeatureInstalled method add the following code:
using (SPSite site = new SPSite("Add your site URL"))
{
    IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent();
    IServiceLocatorConfig typeMappings =
serviceLocator.GetInstance<IServiceLocatorConfig>();
    typeMappings.RegisterTypeMapping<VisualWebPart.IVisualWebPart1,
VisualWebPart.VisualWebPart1>();
}

Now we can give it a try. Deploy the WebPart and add it to the WebPart gallery within Sharepoint. When you place it on a site you should be able to see the names list from the model class inside a list box.

The sample project can be downloaded here.

Monday, October 25, 2010

Sharepoint Guidance Package - ServiceLocator

I recently took a look at the Sharepoint Guidance Package. The package supports Sharepoint developers by providing guidance and pre-defined components. Among these components you find the Service Locator, which can be used to register and find services within Sharepoint. A service may be any class. The Service Locator provides the ability to register types at SPSite and SPFarm level. Types are registered with their Interface used as key:
IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent();
IServiceLocatorConfig typeMappings =
serviceLocator.GetInstance<IServiceLocatorConfig>();
typeMappings.RegisterTypeMapping<IServiceInterface,IServiceImplementation>();
You can retrieve the instance of the Locator anytime by using a static method. Then you get the existing type mappings and add your own to it. You can have the same types on SPFarm and SPSite level. When searching for a type, the locator first searches at the current site and proceeds to the farm level, if no proper type was found.
You may retrieve a type the following way:
IServiceLocator serviceLocator = SharePointServiceLocator.GetCurrent();
ServiceImplementation service = serviceLocator.GetInstance<IServiceInterface>();
There's one thing you have to pay attention, when using the Service Locator and registering types. The Locator accesses the Sharepoint web application settings. To access these settings, it needs elevated privileges. So if you try to register types from within Webparts or controls, that are located on content sites, you will get a security exception. There are two workarounds for that. My preferred one is to use a Feature Receiver and overwrite the feature installed event. Just add an Event Receiver to the existing feature in your project or add a new feature.The installed event will be called, when the feature is installed on the Sharepoint site or farm, which requires an account with elevated privileges. So within the event you can do all your type registering without running in a security exception. The second workaround is to create a website inside the central administration. Have a look at Deploy an Application Page to Central Administration for that solution.

About

Hi Folks!

This Blog is about my experiences with .NET, C#, Sharepoint and maybe other stuff related to programming or architecture. Hope you enjoy it :)