Thursday, February 10, 2011

ASP MVC and SharePoint 2010

Ever since ASP MVC came out, I have been a huge fan. I love how MVC embraces the web instead of abstracting it like web forms do. Being a SharePoint guy however, I am currently stuck with web forms. Now I have to admit that web forms work great for an intranet data oriented solution, and this is also where SharePoint has a lot of strength. I however tend to focus on internet facing web sites where MVC is far superior in my opinion.

It should come as no surprise that I have been wondering how to use MVC in SharePoint, and have tried a few things. There are a number of people who have done the same:

http://sharepointmvc.codeplex.com/
http://vspug.com/mbailey/files/2010/04/Using-ASP.NET-MVC-2-with-Sharepoint-Publishing.pdf

and all are good efforts.

Still, the efforts above all seem like fitting a square peg in a round hole. Not exactly elegant at all times, at least that is my feeling.

So I took another approach at this and think it works quite well. The key, the SharePoint client object model. Yeah, it's that simple. Let me walk you through it.

First, let's fire up Visual Studio and create a new MVC 2 project and make sure to choose .NET 3.5 in the framework drop down.
NOT MVC 3! This is because SharePoint runs on .NET 3.5 so we are currently stuck MVC 2 and no Razor :(




Go though the wizard, create your app with or without a test project (as you wish) and get your MVC app running. Nothing new here.

Now open up the Index view of the home controller, and ensure that the MainContent placeholder content looks like the following:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%= Html.Encode(ViewData["Message"]) %></h2>
    <p>
        To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
    </p>
    <img src="<%=ViewData["image"] %>" alt="" />
</asp:Content>

All this does is add an image tag that we will populate from the ViewData. For the rest it is the same as was generated by default.

Now open up the Home controller and go to the Index action. Here we will add code to talk to SharePoint with the Client object model, and pull out some data.

The code (this code is purely for illustrating a point, it is NOT production quality):

public ActionResult Index()
        {
            // get the client context
            ClientContext clientContext = new ClientContext("http://yoursharepointsite.local");

            // get the web reference
            Web site = clientContext.Web;
            clientContext.Load(site);
       
            // get the pages list reference
            List pages = site.Lists.GetByTitle("Pages");
            clientContext.Load(pages);
 
            // get the item with ID 1
            ListItem page = pages.GetItemById(1);
            clientContext.Load(page);
            clientContext.ExecuteQuery();

            // fish out the property PublishingPageImage
            string image = (string)page["PublishingPageImage"];
            
            // do some string magic to get the URL out of the property value
            int start = image.IndexOf("src=\"")+5;
            string imageUrl = image.Substring(start);
            int end = imageUrl.IndexOf("\"");
            imageUrl = imageUrl.Substring(0, end);
            imageUrl = string.Format("{0}{1}", sourceSite, imageUrl);

            // set the viewdata properties from the page item values
            ViewData["Message"] = string.Format("Title: {0}", site.Title);
            ViewData["Image"] = imageUrl;

            return View();
        }

Let's walk through it:

First we create a client context from a sharepoint site. This particular code is going to a stie at http://yoursharepointsite.local, you need to change this. I should also point out that the SharePoint site at that URL is a standard SharePoint Publishing Portal.

Next we get a reference to the root web, and then to a list called 'Pages' which should be familiar to anyone using SharePoint.

Then we get the first item in that list, just using the ID.

Since this is a standard Publishing Portal, we know that the content type this page uses has a field named 'PublishingPageImage' so we get the string value in this field. This value is the entire img tag which we don't necessarily want so we use a but of string magic to get just the value of the src element.

Lastly we populate some ViewData properties with values we got from the list item.

The magic happens when we run the MVC application. And I mean just run with F5 form Visual Studio. If all is well, you will see the following:


Recognize that image? And the title of my SharePoint site is 'Demo'. That you will have to believe. :)

Now let me point out something. Note the URL in the browser. This site is not running as part of SharePoint, it is not even on the same server! It is running on the built in Visual Studio web server, so it could run pretty much anywhere. As long as the user who is running this code has access to the items on the SharePoint site, this MVC site can run wherever. So in case of an internet site, Just make sure you have your SharePoint site set up for anonymous access, and your MVC site will work just fine.

This is an extremely simple demo example, just to get you started. Having thought about this for a bit, I think I would NOT enable anonymous access on my SharePoint site, and use a nice model in MVC to access SP as a specific user that is meant just for this purpose. This would involve some type of image handler so that images could be accessed by anonymous users, but that is not too hard to create. This approach combined with good caching could allow me to have an MVC site with content coming from SharePoint, but without the end user ever coming in contact with SharePoint. Another blog post perhaps....

Hope this gets your imagination going on how we can get lean and clean HTML and still use SharePoint as a place to store and manage all the content which it is great for.