Sharepoint and MVC

Recently a client asked me if I have experience in SharePoint combined with MVC. My response was "no", but  the question kept running through my mind... So I decided to do some testing and investigate the possibilities of SharePoint and MVC. First of all I want to know why one would use MVC instead of webforms, MVP or MVVM? 
Since this is a lot of material , I will explain in as series of a few posts what the different design patterns are.  In the last post I will sum up the pros and cons of each pattern. I will also answer the question "is it possible to use SharePoint in an MVC pattern"

Improving SharePoint Search

This week I had a question from someone who tried to find a PDF based on keywords in the file-name and it did not show up in the results.

The situation

I found out that this is caused by the (in)famous wordbreaker.
Wordbreaker is a language specific active-x control that breaks compound words (obvious!).
Ie. when i have the browser language set to English, the word "thumbnail" would be broken into "thumb" and "nail". This wordbreaker is designed to improve search results. And when searching for content this works as expected. So if I'm searching for "thumbnail" it will return results with "thumb", "nail" and "thumbnail".
However, when the searchterm is not in the content or meta data (only in the in the filename: not a best practice, I know), the wordbreaker is not working quite as it should.


I'll explain this in the following examples:
I have:

  1. an image called ladybug.jpg
  2. an image called lady bug.jpg
  3. an empty document called ladybug.doc
  4. an empty document called lady bug.doc
  5. a document with ladybug in the content called ladybug.doc

Test1, browser language set to English, query "ladybug"

  1. not shown in result
  2. shown in result
  3. not shown in result
  4. shown in result
  5. shown in result.

Test2, browser language set to Dutch, query "ladybug"

  1. shown
  2. not shown
  3. shown
  4. not shown
  5. shown

So what I've done here is check if it is indeed the wordbreaker by changing the language, furthermore I've added an empty document to test if it isn't the extension / iFilter.
If the word is put in the content, it will show in the results and funny enough also have the filename / title highlighted.


It appears that when no results are found in the content, only the separate words are used to search and not the compound word itself..
Okay, problem is indeed the wordbreaker but how do we fix it? I couldn't find any solution on the internet, although I found plenty of people with the same issue.
Adding quotes doesn't work, maybe I should tell the users to change their browser language?
Nah, c'mon! The wordbreaker helps improve results most of the time, so this not really an option.
Finally, I found a workaround though that I'm happy to share with you guys: when adding a ".*" (dot and asterix) or a ".ext" (dot extension) SharePoint Search does not use the wordbreaker. Pretty lame, but it kinda works.
Hopes this will help!

SP 2010 Totals on calculated columns

Recently I needed to create a participants list where users enter the number of participants (students) split up by gender and class. It also has a "Total" column which calculates the sum of participants. The column looks like this:
So now I needed to display the sum of the "Total" column. That's easy, you might say: just create a view and select the column in the totals section... but... I wouldn't be writing a post if this was the case........
In SharePoint 2010 lists, you still can't display totals on calculated fields. Binging on the subject results in 2 solutions which are both good alternatives. The first solution is creating a Data View Web Part (you can find the screencast from Laura Rogers @wonderlaura here). The disadvantage of this approach is that you must edit the pages in Designer. The second approach is to calculate the total in a workflow after a item is added / modified. Richard Harbridge explains how one can do this in his post here. After I implemented the second option and proudly presented the result to the users, they told me they rather have an excel like view. This pointed out a third option: in the data sheet view, a calculated column can have a total out of the box.
If you don't need the group by etc. and all your users have office installed, a datasheet view is the alternative where you can Total a calculated column out of the box. The lack of this total in a normal view is a bit of a weird "feature" though...

SP 2010 Connected Lookup Fields

SharePoint 2010 introduces some new features regarding lookup fields.
But there are still some things to improve... for instance, SharePoint 2010 still doesn't have filtered lookups and you can only lookup thing in the same sitecollection. In this post I will look at some new features and some things that should be improved (in my opinion).

The new stuff

Concatenate fields

In SharePoint 2010, you can concatenate fields from the remote list when creating a lookup. This means that you can now - for example - create a lookup to a list of contacts which displays the first name, last name, position and email instead of full name or first name or last name.

Data integrity

Another cool thing is data integrity: you can prevent an item from being deleted that has other items connected to it or cascade the delete, ie deleting all dependent items.
Let's say you have a project list and project tasks list, where a task is connected to a project.
So, by deleting a project you can make sure that all that project tasks are also deleted.
Or - if you'd like, to prevent deleting a project with active tasks! which can be very helpful.

Reversed Look up

There's also news on reversed lookup. SharePoint 2010 does not fully support reverse lookup fields, however it does have a new API for getting all related items for a specific list items.
This is how it works: the new API will get all items in SharePoint that have a lookup that is connected to the current item. The API comes with a new web part which you can drop at any listitem viewform to see its connected items.

Things to improve

Reversed Look up

Like I said in the beginning, there are still some things to improve, for instance, if you want real reverse lookup functionality you will have to DIY or buy a third party solution.

Connected Look up

Another thing that is still missing is cascaded lookup: the ability to filter a field based on the content of another field, again one could try something from codeplex or buy a third party solution, but the disadvantage of all these solutions is that none of them support editing in datasheet view, so if you want to bulk edit, for example some meta data in a document library, the fields become read only. This is because the controls are webcontrols and the datasheet view uses an active x from ms-office.

The future

Currently I started with the development of a connected lookup control (SP 2007 and SP 2010) that is fully functioning, also in a datasheet view. I hope to release a first version in two or three months.

SPContent types lack internal names

As you could read in my last post I had a little issue with a feature that create a new list item and writes this to list. However, turning custom errors off didn't help that much: I had everything in a try/catch and I still got a null reference exception.

As I was testing, it turned out that the problem had something to do with the Dutch language pack. So I installed the Dutch language pack on my dev machine (yeah, I know... should have done that from the moment I started developing for this client) and bingo.

The problem was that I assumed the value of SPContentType.Name is an internal name and behaves the same as SPField.

So setting the content type on the list like this:
SPContentTypeId contactTypeId = _currentList.ContentTypes["Contacts"].Id;
Will work for English lists, but will cause a null reference exception for Dutch ones, since the name for this content type is "ContactPersonen" in Dutch...

Instead we need to do it like this:
// 0x0106 : Parent Id for Contacts
SPContentTypeId baseTypeId = new SPContentTypeId("0x01060018141B8367FAC64C9CFF864F77742CCB");
SPContentTypeId contactTypeId = _currentList.ContentTypes.BestMatch(baseTypeId);

The trick is to create a bogus SPContentTypeId with the same parentId as the actual one and then use the BestMatch method of the SPList.ContentTypes.
This way we always get the correct content type Id.

Happy hacking!!

SharePoint & Custom Errors

Since SharePoint is a User centric product, it has custom error pages by default. Recently I had a bug in one of my features and only on the productional environment. The error message was "An unexpected error has occurred" with nothing written to log files, trace or the event log.... Great!

Now, you could debug the problem by adding comment & funneling the problem, but that didn't help in this case, so I had to loose the custom error page.

This is what needs to change in the web.config, first modify the custom error entry:
<customErrors mode=“Off“/>

and, if you want to see the callstack, change it to
<SafeMode MaxControls=“200“ CallStack=“true“…

This will change the custom "An unexpected error has occurred" to the standard ASP.Net error page!

Don't forget to change it back when you're done debugging!!!

syntaxhighlight test

This is a quick test to see if the syntax highlighter is working...
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using BvDWeB.Classes.Core;
using BvDWeB.Classes.Models;

namespace BvDWeB.UI
 public partial class _Default : Page
  private BvDSystem _bvdSystem;
  protected void Page_Load(object sender, EventArgs e)
    if(Session["BvDSystem"] == null)
    _bvdSystem = (BvDSystem) Session["BvDSystem"];
    if (Request["systemChoosen"] != null)
     bvdTree.Value = _bvdSystem.ActiveSystem.TreeToUse;
     bvdLoadBvDUserSystem.Value = _bvdSystem.LoadBvDUserSystem.ToString();

     if (!_bvdSystem.LoadBvDUserSystem)
      foreach (BvDSystem system in _bvdSystem.Systems)
       ListItem item = new ListItem(system.Title, system.Config, false);
       item.Attributes.Add("systemIndex", system.SystemIndex.ToString());
      bvdTree.Value = _bvdSystem.ActiveSystem.TreeToUse;