Working with SPFielduserValue in item event receivers - some pitfalls.

Recently I posted about accessing lookup fields in item event receivers and about some pitfalls when you are using it.

Next part is about using PeopleEditor (or people picker as it sometimes called). Let's add new field to our list and see what will happen in event receivers.

First, ItemAdding:

In AfterProperties you can see "7". "7"?! Why? Because SPFieldUser that we was added also an a lookup field. Lookup list for this field is a hidden UserInformationList that trackes a part of user-related information, such as login name, display name, email, and some other. "7" is id of user in UserInformationList. You can get SPUser object by using method web.SiteUsers.GetByID(id), or you can use SPFieldUserValue to get user.

In ItemAdded you can see that our lookup has a value - this a login name of a user:

But sometimes you may encounter with problem, when user may not in UserInformationList. I delete user from this list using this small PS script:

$web = Get-SPWeb "http://localhost/sites/test"
$user = $web.SiteUsers["DOMAIN\testuser"]
$web.SiteUsers.RemoveByID($user.ID)

If user not in list, what Id we get in ItemAdding event? Here is an answer:

Id is equal to -1, and login name as value. This is a correct, because SharePoint doesn't know yet what Id has the user. In this situation we must call method web.EnsureUser(loginname) to ensure that user will be added to UserInformaionList. But if you use SPFielduserValue class, you may to initalize user, you may faced with problem. User property will be equal to null, and you can't get access to it.

But in ItemAdded this user already has an Id, and he is added to UserInformationList:

If you will use SPFieldUserValue object and access User property in ItemAdding, it returns null, because user not actually in UserInformatinList. If you go to Reflector, you can see, that SharePoint try to get user with no any exception thrown if it fails:

You must call EnsureUser, to add user when lookupId == -1.

To make process a little bit easier I write a simple extension for SPWeb object, that always return actual SPUser depends on lookup string that you pass to it:

public static SPUser EnsureUserByLookupValue(this SPWeb web, string userValue)
{
	if(string.IsNullOrEmpty(userValue))
	{
		return null;
	}
	var user = new SPFieldUserValue(web, userValue);
	return user.LookupId == -1 ? web.EnsureUser(user.LookupValue) : user.User;
}

And extension that returns SPFieldUserValue with actual User property:

public static SPFieldUserValue EnsureSPFieldUserValueByLookupValue(this SPWeb web, string userValue)
{
	if (string.IsNullOrEmpty(userValue))
	{
		return null;
	}
	var userFieldValue = new SPFieldUserValue(web, userValue);
	if (userFieldValue.LookupId == -1)
	{
		var user = web.EnsureUser(userFieldValue.LookupValue);
		return new SPFieldUserValue(web, user.ID, user.LoginName);
	}
	return userFieldValue;
}

And you may know, that PeoplePicker also accept multivalues, that's why here is an extension method that returns SPFieldUserValueCollection with actual User properties:

public static SPFieldUserValueCollection EnsureAllUsersValues(this SPWeb web, string spFieldUserValueCollection)
{
	if(string.IsNullOrEmpty(spFieldUserValueCollection))
	{
		return new SPFieldUserValueCollection(web, spFieldUserValueCollection);
	}
	var ensureUsers = new SPFieldUserValueCollection();
	var users = new SPFieldUserValueCollection(web, spFieldUserValueCollection);
	if (users.Count == 0)
		return users;
	foreach (SPFieldUserValue userValue in users)
	{
		//if lookupId == -1 user not in user information list, so we add it
		if (userValue.LookupId == -1)
		{
			var user = web.EnsureUser(userValue.LookupValue);
			ensureUsers.Add(new SPFieldUserValue(web, user.ID, user.LoginName));
		}
		else
		{
			ensureUsers.Add(userValue);
		}
	}
	return ensureUsers;
}

So, I tried to cover some interesting and sometimes not expected moments of SharePoint lookup fields, especially when you accessing it in item event receivers. Hope this information helps.