Wesley Bakker

Interesting things I encounter doing my job...

Sponsors

News

Wesley Bakker
motion10
Rivium Quadrant 151
2909 LC Capelle aan den IJssel
Region of Rotterdam
The Netherlands
Phone: +31 10 2351035

(feel free to chat with me)

Add to Technorati Favorites

November 2008 - Posts

SharePoint Data Access Layer Part II

Generics

In part one I talked about strongly typed access to SharePoint list items and we did set a base for how to retrieve values in a more strongly typed fashion. In part two I'll extend this example and show you how to retrieve wrapped items from a SharePoint list.

The solution

Our final solution is created out of three classes which all have a specific use.

  • Extensions class;
  • ListItemWrapper class:
  • ListWrapper class

The Extensions class is a static class which contains extension methods for SPItem, SPListItem and SPListItemCollection. These extension methods help us throughout our SharePoint development and can be extended to contain even more helpfull methods.

The ListItemWrapper class is an abstract base class that all of our custom wrappers should inherit. It provides us with the possibility to access properties of list items in a strongly typed fashion

The ListWrapper class is a generic class that helps us in querying sharepoint lists. Returning wrapped list items.

Sample code

If you're not interested in large chunkes of code you can find a download link with the full sourcecode and the end of this post. So be my guest and download it. If you however would like to get a feel of how you can use this simple class library hang on with me and I'll show you how to use it with a real life example.

Outline: We have a SharePoint site for a car selling business. On this site we have a SharePoint list with content types and one of those content types is the Car type. This Car content type has an id of '0x01000D483DA73079A341828E6E216045019A0078881191FD5D1642839403A59FC701C0' and several columns of which we take 3 columns for our example. It has an Url column that contains the website url of the brand named 'ManufacturerUrl'. It has an Integer column that contains the mileage named 'Mileage'. And it contains a LookupMulti column that contains a list of accessoiries named 'Accessoiries'

ItemWrapper

The only way we could normally access these fields in our code is by the field indexer on SPListItem. Which causes a lot of problems.

  • I'd have the names of these fields scattered all over my code;
  • I'd have to remember which types these fields contain and cast them every time;
  • I'd have to check the content type:
  • I'd have to check for null values

And that's just to name a few. That's why we create a wrapper. And our wrapper will take care of all of thos issues mentioned before. Just have a look at the next code.

public sealed class CarWrapper : ListItemWrapper {

        private static readonly string contentTypedId = "0x01000D483DA73079A341828E6E216045019A0078881191FD5D1642839403A59FC701C0";

 

        public CarWrapper(SPListItem listItem)

            : base(listItem, true, ContentTypeId) {

        }

 

        public static string ContentTypeId {

            get {

                return contentTypedId;

            }

        }

 

        public int Mileage {

            get {

                return ListItem.GetValue<int>("Mileage", 0);

            }

        }

 

        public SPFieldUrlValue ManufacturerUrl {

            get {

                return ListItem.GetUrlFieldValue("ManufacturerUrl");

            }

        }

 

        public SPFieldLookupValueCollection Accessoiries {

            get {

                return ListItem.GetLookupFieldValueCollection("Accessoiries");

            }

        }

 

        public static IEnumerable<CarWrapper> GetItemsByContentTypeId(string url) {

            return new ListWrapper<CarWrapper>(url).GetItemsByContentTypeId(ContentTypeId);

        }

 

        public static IEnumerable<CarWrapper> GetItemsByContentTypeId(SPList list) {

            return new ListWrapper<CarWrapper>(list).GetItemsByContentTypeId(ContentTypeId);

        }

    }

What you see here is a simple class that inherits from ListItemWrapper. We set the ContentTypeId to the Id of the Car content type and we add our three columns as properties. To retrieve the values in our properties we simply use the extension methods from our library. So from now on we can actualy access our properties strongly typed, without knowing exactly what field the value comes from. Just like this:

CarWrapper someCar = new CarWrapper(someSPListItem);

 

Console.WriteLine(someCar.Mileage + " miles");

Console.WriteLine("Accessoiries:");

foreach (var accessory in someCar.Accessoiries) {

    Console.WriteLine(accessory.LookupId + " : " + accessory.LookupValue);

}

You must admit that this does look a lot better already don't you? This alows for much faster development. Do not forget the intellisense you get when you're working with strongly typed code and the fact that there's an automated check to validate the content type id(which is optional). So we do have a wrapper but we are not finished yet. Why not automagically wrap all list items of a list? It's very very easy beacause of the generic ListWrapper class.

ListWrapper

Most of the time we would like to itterate through a number of list items in a SharePoint list. And if possible in a strongly typed fashion right? Well if you did take a good look at the wrapper class you would have noticed that there are two methods in there that return an IEnumerable of type CarWrapper simply by the Url or an instance of a SPList. I used the generic ListWrapper class to do so.

Imagine you have just one list in you site where you store all the cars. You could extend the wrapper with static methods that return IEnumerables of CarWrapper without the input of a url. Have a look at this:

private static readonly string carUrl = "http://mysite/lists/cars";

 

public static IEnumerable<CarWrapper> GetAllCars() {

    return new ListWrapper<CarWrapper>(carUrl).GetItemsByContentTypeId(ContentTypeId);

}

Wrap it up

I would like to point out is that this library is not your end point. You can extend your wrappers to allow for updating or add a lot of predefined queries. All I realy want you guys and girls to do is work strongly typed! Because I don't know about you but I'm realy tired of reading lines like 'int mil = (int)item["Mileage"]' all over the code. We should not work like that do we?

Please do leave a comment if you do or do not like my idea of working with SharePoint list items.

Happy coding!

Wesley

Posted: Nov 19 2008, 02:12 PM by webbes | with 4 comment(s)
Filed under: , ,
SharePoint DataLayers

SharePoint DataLayers

It's great to see how flexible SharePoint 2007 actually is. You can create lists that can contain all sorts of data. Add columns, create data types, create content types etc. But great flexibility has a price. All to often I lay my eyes on code that's not readeable, manageable or strongly typed. Which leaves a lot of room for errors. In this post I'll drop some of my ideas on how to create and access lists in a SharePoint 2007 solution. Which will leave you with strongly typed access to your content types.

New features of C# 3.0

I don't know about you but I really don't like all the different way's of retrieving SPItem values. The one field needs a cast, the other field needs an object instantiation. Another problem is that I need to check if a field exists before trying to retrieve it's value. This leaves a lot of room for error. So I'm realy not happy with the SPItem class. Fortunately there's a great new feature called Extension Methods. Extension Methods allow us to extend the SPItem class so let's start with that. I created a simple Utility class that allows me some easier access to fields. Oh and it's strongly typed right away. Have a look at this.

using System;

using System.Globalization;

using Microsoft.SharePoint;

 

namespace SharePoint2007Base {

    /// <summary>

    /// The ItemUtility class contains some extension methods which help to retrieve values from fields

    /// </summary>

    public static class ItemUtility {

        private static object GetFieldValue(this SPItem item, string fieldName) {

            if (item == null) {

                throw new ArgumentNullException("item");

            }

 

            if (string.IsNullOrEmpty(fieldName)) {

                throw new ArgumentNullException("fieldName");

            }

 

            object retVal = null;

 

            if (item.Fields.ContainsField(fieldName)) {

                retVal = item[fieldName];

            }

 

            return retVal;

        }

 

        /// <summary>

        /// Gets the value.

        /// </summary>

        /// <typeparam name="T"></typeparam>

        /// <param name="item">The item.</param>

        /// <param name="fieldName">Name of the field.</param>

        /// <param name="defaultValue">The default value.</param>

        /// <returns></returns>

        public static T GetValue<T>(this SPItem item, string fieldName, T defaultValue) {

            T retVal = defaultValue;

 

            object fieldValue = item.GetFieldValue(fieldName);

            if (fieldValue != null) {

                retVal = (T)Convert.ChangeType(fieldValue, typeof(T), CultureInfo.InvariantCulture);

            }

 

            return retVal;

        }

 

        /// <summary>

        /// Gets the lookup field value.

        /// </summary>

        /// <param name="item">The item.</param>

        /// <param name="fieldName">Name of the field.</param>

        /// <returns></returns>

        public static SPFieldLookupValue GetLookupFieldValue(this SPItem item, string fieldName) {

            SPFieldLookupValue retVal = null;

 

            object fieldValue = item.GetFieldValue(fieldName);

            if (fieldValue != null) {

                retVal = fieldValue as SPFieldLookupValue;

            }

 

            return retVal;

        }

 

        /// <summary>

        /// Gets the lookup field value collection.

        /// </summary>

        /// <param name="item">The item.</param>

        /// <param name="fieldName">Name of the field.</param>

        /// <returns></returns>

        public static SPFieldLookupValueCollection GetLookupFieldValueCollection(this SPItem item, string fieldName) {

            SPFieldLookupValueCollection retVal = new SPFieldLookupValueCollection();

 

            object fieldValue = item.GetFieldValue(fieldName);

            if (fieldValue != null) {

                retVal = fieldValue as SPFieldLookupValueCollection;

            }

 

            return retVal;

        }

 

        /// <summary>

        /// Gets the multiple choice field value.

        /// </summary>

        /// <param name="item">The item.</param>

        /// <param name="fieldName">Name of the field.</param>

        /// <returns></returns>

        public static SPFieldMultiChoiceValue GetMultipleChoiceFieldValue(this SPItem item, string fieldName) {

            SPFieldMultiChoiceValue retVal = new SPFieldMultiChoiceValue();

 

            string fieldValue = item.GetValue<string>(fieldName, null);

            if (!string.IsNullOrEmpty(fieldValue)) {

                retVal = new SPFieldMultiChoiceValue(fieldValue);

            }

 

            return retVal;

        }

 

        /// <summary>

        /// Gets the URL field value.

        /// </summary>

        /// <param name="item">The item.</param>

        /// <param name="fieldName">Name of the field.</param>

        /// <returns></returns>

        public static SPFieldUrlValue GetUrlFieldValue(this SPItem item, string fieldName) {

            SPFieldUrlValue retVal = null;

 

            string fieldValue = item.GetValue<string>(fieldName, null);

            if (!string.IsNullOrEmpty(fieldValue)) {

                retVal = new SPFieldUrlValue(fieldValue);

            }

 

            return retVal;

        }

 

        /// <summary>

        /// Gets the user field value.

        /// </summary>

        /// <param name="item">The item.</param>

        /// <param name="fieldName">Name of the field.</param>

        /// <returns></returns>

        public static SPFieldUserValue GetUserFieldValue(this SPItem item, string fieldName) {

            SPFieldUserValue retVal = null;

 

            string fieldValue = item.GetValue<string>(fieldName, null);

            if (!string.IsNullOrEmpty(fieldValue)) {

                SPFieldUser userField = item.Fields.GetField(fieldName) as SPFieldUser;

                retVal = userField.GetFieldValue(fieldValue) as SPFieldUserValue;

            }

 

            return retVal;

        }

 

        /// <summary>

        /// Gets the user field value collection.

        /// </summary>

        /// <param name="item">The item.</param>

        /// <param name="fieldName">Name of the field.</param>

        /// <returns></returns>

        public static SPFieldUserValueCollection GetUserFieldValueCollection(this SPItem item, string fieldName) {

            SPFieldUserValueCollection retVal = new SPFieldUserValueCollection();

 

            object fieldValue = item.GetFieldValue(fieldName);

            if (fieldValue != null) {

                retVal = fieldValue as SPFieldUserValueCollection;

            }

 

            return retVal;

        }

    }

}

This utility class makes retrieval of field values pretty easy already. We can now get a string value like this string fieldValue = item.GetValue<string>(fieldName, null); which is a great improvement tmho.

Let's wrap it up!

No, this is not the end of my post. In this part we wil create a simple wrapper around a SPListItem to decorate it with some properties. The decorator pattern is a widely used pattern. My implementation is a lazy one however. I do not pass on every single property and method of a SPListItem. I simple allow access to the underlying list item by a property called ListItem and add some extra strongly typed properties.

I do have a very good reason for doing this beyond making it even easier to retrieve values. I do not want to have my field names scattered all over my code files. First of all I do not want to know the field names of all my different content types by hart. But imagine that my content type changes and the field name with it. I would have to run through my code and find all refrences to that field. Just have a look at this code.

using Microsoft.SharePoint;

using SharePoint2007Base;

 

namespace SharePoint2007BaseConsoleTest {

    /// <summary>

    /// SampleListItemWrapper is a wrapper for a list item which enables us to acces it's fields strongly typed.

    /// </summary>

    public class SampleListItemWrapper {

        private readonly SPListItem listItem;

 

        /// <summary>

        /// Initializes a new instance of the <see cref="SampleListItemWrapper"/> class.

        /// </summary>

        /// <param name="listItem">The list item to wrap.</param>

        public SampleListItemWrapper(SPListItem listItem) {

            this.listItem = listItem;

        }

 

        /// <summary>

        /// Gets the list item.

        /// </summary>

        /// <value>The list item.</value>

        public SPListItem ListItem {

            get {

                return listItem;

            }

        }

 

        /// <summary>

        /// Gets the custom string field.

        /// </summary>

        /// <value>The custom string field.</value>

        public string CustomStringField {

            get {

                return ListItem.GetValue<string>("CustomStringField", string.Empty);

            }

        }

 

        /// <summary>

        /// Gets the custom int field.

        /// </summary>

        /// <value>The custom int field.</value>

        public int CustomIntField {

            get {

                return ListItem.GetValue<int>("CustomIntField", 0);

            }

        }

 

        /// <summary>

        /// Gets the custom URL field.

        /// </summary>

        /// <value>The custom URL field.</value>

        public SPFieldUrlValue CustomUrlField {

            get {

                return ListItem.GetUrlFieldValue("CustomUrlField");

            }

        }

    }

}

Abstraction

This is way cool. We do have realy strongly typed access to our list item values with the field names defined in only one place! So if the underlying content type changes all we need to do is change one class only! Allthough this is way cool. This does create another problem called code duplication. Every wrapper class hass the same contructor and the same ListItem property. So let's add a little abstraction to our mix. How about this.

/// <summary>

/// The ListItemWrapper class form the base for ListItemWrapper classes

/// </summary>

public abstract class ListItemWrapper {

    private readonly SPListItem listItem;

 

    /// <summary>

    /// Initializes a new instance of the <see cref="ListItemWrapper"/> class.

    /// </summary>

    /// <param name="listItem">The list item.</param>

    protected ListItemWrapper(SPListItem listItem) {

        if (listItem == null) {

            throw new ArgumentNullException("listItem");

        }

 

        this.listItem = listItem;

    }

 

    /// <summary>

    /// Gets the list item.

    /// </summary>

    /// <value>The list item.</value>

    public SPListItem ListItem {

        get {

            return listItem;

        }

    }

}

Our sample wrapper and any other wrapper we create can now inherit from this base class.

Generics

This is just the first part of a two part blog post. In the next post I'll show you how you can add some generics and retrieve your list items in the following fashion:

foreach (SampleWrapper wrappedItem in SampleWrapper.GetDefaultViewItems("http://mysite/lists/mylist")) {

                Console.WriteLine(wrappedItem.CustomStringField);

            }

Happy coding!

Cheers,

Wes

Posted: Nov 17 2008, 10:22 AM by webbes | with 5 comment(s)
Filed under: , ,
More Posts