| Subcribe via RSS

Simplify reading and writing field values of type SPFieldUser, SPFieldUrl and SPFieldLookup using extension methods

Februar 8th, 2009 Posted in .NET, SharePoint

To read or write values to fields of type SPFieldUser or SPFielLookup is an annoying task because you have to create field value objects bevore you can write your values into a field. The following post descripes how you can encapsulate those tasks to extension methods and slim your code to make it more readable.

SPFieldUser

The following code displays a couple of extension methods extending the SPListItem class for simple reading and writing access to fields of type SPFieldUser.

using System;
using System.Collections.Generic;
using Microsoft.SharePoint;
 
namespace ExtensionMethods
{
    public static class SPListItemExtensions
    {
        /// <summary>
        /// Returns the login name of an User-Field.
        /// </summary>
        public static string GetFieldValueUserLogin(this SPListItem item, 
          string fieldName)
        {
            if (item != null)
            {
                SPFieldUserValue userValue = 
                  new SPFieldUserValue(
                    item.Web, item[fieldName] as string);
                return userValue.User.LoginName;
            }
            else
            {
                return string.Empty;
            }
        }
 
        /// <summary>
        /// Sets the value of a User-Field to a login name.
        /// </summary>
        public static void SetFieldValueUser(this SPListItem item, 
          string fieldName, string loginName)
        {
            if (item != null)
            {
                item[fieldName] = item.Web.EnsureUser(loginName);
            }
        }
 
        /// <summary>
        /// Sets the value of a User-Field to an SPPrincipal 
        /// (SPGroup or SPUser).
        /// </summary>
        public static void SetFieldValueUser(this SPListItem item, 
          string fieldName, SPPrincipal principal)
        {
            if (item != null)
            {
                item[fieldName] = principal;
            }
        }
 
        public static void SetFieldValueUser(this SPListItem item, 
          string fieldName, IEnumerable<SPPrincipal> principals)
        {
            if (item != null)
            {
                SPFieldUserValueCollection fieldValues = 
                  new SPFieldUserValueCollection();
 
                foreach (SPPrincipal principal in principals)
                {
                    fieldValues.Add(
                      new SPFieldUserValue(
                        item.Web, principal.ID, principal.Name));
                }
                item[fieldName] = fieldValues;
            }
        }
 
        /// <summary>
        /// Sets the value of a multivalue User-Field to 
        /// a list of user names.
        /// </summary>
        public static void SetFieldValueUser(this SPListItem item, 
          string fieldName, IEnumerable<string> loginNames)
        {
            if (item != null)
            {
                SPFieldUserValueCollection fieldValues = 
                  new SPFieldUserValueCollection();
 
                foreach (string loginName in loginNames)
                {
                    SPUser user = item.Web.EnsureUser(loginName);
                    fieldValues.Add(
                      new SPFieldUserValue(
                        item.Web, user.ID, user.Name));
                }
 
                item[fieldName] = fieldValues;
            }
        }
    }
}

The following lines of code demonstrate how these extension methods can be used.

using ExtensionMethods;
 
SPList list = web.Lists["MyList"];
SPListItem item = list.Items[0];
 
// Get a user login of a SPFieldUser field
string userLogin = item.GetFieldValueUserLogin("Author");
 
// Set a SPFieldUser field to a user using the login
item.SetFieldValueUser("Person", "alexander.bruett");
 
// Set a SPFieldUser field that allows multiple values to a list of users
string[] persons = { "alexander.bruett", "paul.panzer" };
item.SetFieldValueUser("Mehrere Personen", persons);
 
// Set a SPFieldUser field to a SPUser
SPUser user = web.Users[1];
item.SetFieldValueUser("Person", user);
 
// Set a SPFieldUser field to a list of SPPrincipal
SPGroup group = web.Groups[0];
SPUser user = web.Users[0];
SPUser user2 = web.EnsureUser("alexander.bruett");
SPUser user3 = web.EnsureUser("Domain Users"); ;
SPPrincipal[] principals = { group, user, user2, user3 };
item.SetFieldValueUser("AssignedTo", principals);

Especially setting fields that allow multiple values now needs only a couple of code lines.

SPFieldLookup

The SPFieldLookup field is a little bit more complicated if you want to set the field only by the lookup string. You have to query the ID of the lookup value before you can set the field value. The following extension methods simplify this task.

/// <summary>
/// Returns the value of a Lookup Field.
/// </summary>
private static string GetFieldValueLookup(this SPListItem item, 
    string fieldName)
{
    if (item != null)
    {
        SPFieldLookupValue lookupValue = 
            new SPFieldLookupValue(item[fieldName] as string);
        return lookupValue.LookupValue;
    }
    else
    {
        return string.Empty;
    }
}
 
/// <summary>
/// Returns the value of a Lookup-Field with multiple values.
/// </summary>
public static IEnumerable<string> GetFieldValueLookupCollection(
    this SPListItem item, string fieldName)
{
    List<string> result = new List<string>();
    if (item != null)
    {
        SPFieldLookupValueCollection values = 
            item[fieldName] as SPFieldLookupValueCollection;
 
        foreach (SPFieldLookupValue value in values)
        {
            result.Add(value.LookupValue);
        }
    }
    return result;
}
 
/// <summary>
/// Returns the SPFieldLookupValue instance of a lookup value. 
/// The ID value will be obtained using SPQuery.
/// </summary>
private static SPFieldLookupValue GetLookupValue(
    SPWeb web, SPFieldLookup field, string lookupValue)
{
    string queryFormat = 
        @"<Where>
            <Eq>
                <FieldRef Name='{0}' />
                <Value Type='Text'>{1}</Value>
            </Eq>
          </Where>";
 
    string queryText = 
        string.Format(queryFormat, field.LookupField, lookupValue);
    SPList lookupList = web.Lists[new Guid(field.LookupList)];
 
    SPListItemCollection lookupItems = 
        lookupList.GetItems(new SPQuery() { Query = queryText });
 
    if (lookupItems.Count > 0)
    {
        int lookupId = 
            Convert.ToInt32(lookupItems[0][SPBuiltInFieldId.ID]);
 
        return new SPFieldLookupValue(lookupId, lookupValue);
    }
    else
    {
        return null;
    }
}
 
/// <summary>
/// Sets the value of a Lookup-Field.
/// </summary>
public static void SetFieldValueLookup(
    this SPListItem item, string fieldName, string lookupValue)
{
    if (item != null)
    {
        SPFieldLookup field = 
            item.Fields.GetField(fieldName) as SPFieldLookup;
        item[fieldName] = GetLookupValue(item.Web, field, lookupValue);
    }
    else
    {
        item[fieldName] = null;
    }
}
 
/// <summary>
/// Set the values of a Lookup-Field with multiple values allowed.
/// </summary>
public static void SetFieldValueLookup(this SPListItem item, 
    string fieldName, IEnumerable<string> lookupValues)
{
    if (item != null)
    {
        SPFieldLookup field = 
            item.Fields.GetField(fieldName) as SPFieldLookup;
 
        SPFieldLookupValueCollection fieldValues = 
            new SPFieldLookupValueCollection();
 
        foreach (string lookupValue in lookupValues)
        {
            fieldValues.Add(
                GetLookupValue(item.Web, field, lookupValue));
        }
        item[fieldName] = fieldValues;
    }
}

That’s been a lot of code but take a look at the following lines of code. Especially the setting task becomes very easy now.

// Reads the value of a SPFieldLookup field
string value = item.GetFieldValueLookup("LookupFieldName");
 
// Read lookup values of a SPFieldLookup field 
// with multiple values allowed
IEnumerable<string> values = 
  item.GetFieldValueLookupCollection("MultiLookupFieldName");
 
// Set the value of a SPFieldLookup field
item.SetFieldValueLookup("LookupFieldName", "LookupValue");
 
//Set the value of a SPFieldLookup field with multiple values allowed
string[] values = {"Hamburg", "London" };
item.SetFieldValueLookup("MultiLookupFieldName", values);

SPFieldUrl

Setting and getting SPFieldUrl field values is very simple:

/// <summary>
/// Returns the value of an Url-Field.
/// </summary>
public static string GetFieldValueUrl(
    this SPListItem item, string fieldName)
{
    if (item != null)
    {
        SPFieldUrlValue urlValue = 
            new SPFieldUrlValue(item[fieldName] as string);
        return urlValue.Url;
    }
    else
    {
        return string.Empty;
    }
}
 
/// <summary>
/// Sets the value of an URL-Field.
/// </summary>
public static void SetFieldValueUrl(this SPListItem item, 
    string fieldName, string url, string description)
{
    if (item != null)
    {
        item[fieldName] = 
            new SPFieldUrlValue() 
                { 
                    Description = description, 
                    Url = url 
                };
    }
}

The following lines of code show how to get and set values of the SPFieldUrl field using the extension methods above:

// Get the url of a SPFieldUrl field
string url = item.GetFieldValueUrl("URL");
 
// Set the url and description of a SPFieldUrl field
item.SetFieldValueUrl("URL", "http://www.alexbruett.net", "Alex' Blog");

Conclusion

Using extension methods to simplify the access to complex and SharePoint list fields makes your code more readable and reduces the lines of code.

You could also use helper classes instead of extension methods but you will have to write more code to call a helper method instead of calling a extension method.

11 Responses to “Simplify reading and writing field values of type SPFieldUser, SPFieldUrl and SPFieldLookup using extension methods”

  1. Keith Dahlby Says:

    Some great examples! One suggestion…for GetFieldValueLookupCollection you could use an iterator instead of allocating your own list:

    SPFieldLookupValueCollection values =
    item[fieldName] as SPFieldLookupValueCollection;

    foreach (SPFieldLookupValue value in values)
    yield return value.LookupValue;

    Or just use LINQ:

    return from SPFieldLookupValue v in values select v.LookupValue;

    Cheers ~
    Keith


  2. chris Says:

    You may need to add the code below, in case you get an error message (Cannot define a new extension method because the compiler required type ‘System.Runtime.CompilerServices.ExtensionAttribute’ cannot be found.)

    namespace System.Runtime.CompilerServices {
    [AttributeUsage(AttributeTargets.Method)]
    public class ExtensionAttribute : Attribute {
    }
    }


  3. Jeremy Thake Says:

    It’s really nice to see SharePoint Developers working with the extension methods to enhance the SharePoint Object Model! Great work. I have added this code snippet (referencing this post) to the SharePointDevWiki.com as it’ll be great to get some collaboration on other methods that could be added!

    (let me know if you want me to take it down)

    Thanks!


  4. Vadim Dzyuban Says:

    Hi Alex,
    Sharepoint lookup field mapping to a remote lookup list

    Doing research and experimenting with a lookup field I’ve found that a lookup field (a site column which gets their data from a list) and a lookup list (from which a lookup field gets their data) must belong to the same site (or different sites if sites are a parent and child) within a site collection. There are also the cross-sites lookup field solutions where a lookup field and a lookup list might belong to different sites but again within the same website/site collection. All these known implementations do not allow separate a lookup field and a lookup list meaning to have them on different web applications and working together.
    Is this correct or am I missing something?

    There might be another scenario where a lookup field should be deployed on a few hundred client’s sites and these clients’ lookup fields must get their data from a remote lookup list being resided on separate admin web application. In this case remote lookup list might be maintained at one central place (admin web app) and feeds updated data for all clients’ lookup fields. The point here is to avoid creation of hundreds instances of a remote lookup list on the clients’ sites. Client sites’ lookup fields should map directly to a remote lookup list being resided on separate admin web application.
    Any idea, advise, discussion or references about a lookup field mapping to a remote lookup list implementation using CAML/C#/webservices will be appreciated.

    Thanks.
    Vadim.


  5. Alexander Brütt Says:

    Hi Vadim,
    if you want to create a cross-site lookup field you have to create a custom lookup field based on the SPFieldLookup field. In that custom field you can offer the user to configure the website-url and a list name for the lookup list and use these values to fetch the data from that list.
    If you haven’t created a custom field yet, try it :)

    http://msdn.microsoft.com/en-us/library/bb684919.aspx

    I’d also give a shot on the Business Data Catalog. I haven’t used it already but I once saw a Web Cast where a guy showed how to import an external data source into SharePoint and use its data as a lookup column. Everything without coding.

    Maybe this information helps you to go into the right direction :)

    Regards,
    Alex


  6. Ramone Hamilton Says:

    I just wanted to drop a line letting you know how much this has helped me. I’m in the process of creating a Sharepoint ORM tool and was currently handling all three of these fields incorrectly. Keep up the good work.

    Regards,
    Ramone


  7. Steve Bloomer Says:

    Your post was extremely helpful in getting other code to work. I apologise for bothering you with a problem, but this custom field code (to allow selection of only groups that does not appear to be available any other way) I cannot get it to work: on new it just creates a value that displays like 10;#

    I am am ashamed that this is VB code but I have not bitten the bullet to change to c# yet:

    Option Explicit On
    Option Strict On

    Imports System
    Imports System.Runtime.InteropServices

    Imports Microsoft.SharePoint
    Imports Microsoft.SharePoint.WebControls

    Imports System.Web.UI
    Imports System.Web.UI.WebControls
    Imports System.Web.UI.HtmlControls
    Imports Microsoft.VisualBasic

    Namespace pccCustomFields2

    ‘ TODO: Replace, as needed, “TextField” with some other class derived from Microsoft.SharePoint.WebControls.BaseFieldControl.
    _
    _
    Public Class pccAllGroupsFieldControl
    Inherits UserField
    Private glist As ListBox
    Private table As HtmlTable

    Protected Overrides Sub CreateChildControls()
    MyBase.CreateChildControls()
    Me.table = New HtmlTable
    Dim row As New HtmlTableRow
    table.Rows.Add(row)

    Dim cell As HtmlTableCell = Nothing

    If Me.ControlMode = SPControlMode.Edit Or Me.ControlMode = SPControlMode.[New] Then
    cell = New HtmlTableCell
    cell.ColSpan = 2
    cell.Attributes(“class”) = “ms-formdescription”
    cell.InnerText = “Choose a group:”
    row.Cells.Add(cell)
    row = New HtmlTableRow
    cell = New HtmlTableCell
    Me.glist = New ListBox
    With Me.glist
    Dim site As SPSite = SPContext.GetContext(Me.Context).Site
    For Each g As SPGroup In site.RootWeb.SiteGroups
    .Items.Add(g.Name)
    Next
    End With
    If IsNothing(Me.ItemFieldValue) Then
    If Me.glist.Items.Count > 0 Then
    Me.glist.SelectedIndex = 0
    End If
    Else
    Dim lsName As String = CType(Me.ItemFieldValue, SPFieldUserValue).User.Name
    If Not String.IsNullOrEmpty(lsName) Then
    Me.glist.SelectedValue = lsName
    End If
    End If
    cell.Controls.Add(Me.glist)
    row.Cells.Add(cell)
    table.Rows.Add(row)
    End If
    cell = New HtmlTableCell
    Dim lit As New LiteralControl
    Dim group As String = String.Empty
    Dim gObj As Object = Me.ItemFieldValue
    If Not IsNothing(gObj) Then
    If Not IsNothing(CType(gObj, SPFieldUserValue).User) Then
    group = CType(gObj, SPFieldUserValue).User.Name
    End If
    End If
    cell.Controls.Add(lit)
    row.Cells.Add(cell)
    MyBase.Controls.Add(table)
    End Sub

    Protected Overrides Sub Render(ByVal output As System.Web.UI.HtmlTextWriter)
    Me.table.RenderControl(output)
    End Sub

    Public Overrides Sub UpdateFieldValueInItem()
    Me.EnsureChildControls()
    If glist.Items.Count > 0 Then
    Dim site As SPSite = SPContext.GetContext(Me.Context).Site
    For Each g As SPGroup In site.RootWeb.SiteGroups
    If g.Name = glist.SelectedValue Then
    Dim lsValue As String = g.ID & “;#” & g.Name
    Dim x As New SPFieldUserValue(site.RootWeb, g.ID, g.Name)
    ‘ Me.Value = lsValue
    Me.Value = x
    MyBase.UpdateFieldValueInItem()
    Exit For
    End If
    Next
    End If

    End Sub

    End Class

    End Namespace


  8. hd Says:

    fantastic. this is what i was looking for. for hours i was stranded. i wanted to update values in a look up field. jus the cod i was looking for
    keep going.. great !!!!!!!!! expect much more code snippets from u


  9. Janey Wang Says:

    Your post is really helpful. It saves me a lot of time in reading and setting the values of a Lookup-Field with multiple values allowed. Thank you!


  10. Dave Says:

    Hi Alex,

    Firstly – very cool stuff.

    I do have a question. The extension methods are doing a null check on the item for which they are extending.

    Is that necessary? If the item itself is null, the calling of the method is impossible anyway. Is there a Microsoft “Best Practice for this scenario?”


  11. Mano Mangaldas Says:

    Nice work, thanks for sharing.

    -mano


Leave a Reply