Error Handling In Office 365 And SharePoint Online

I am sure there is a lot of guidance out there regarding this subject. Personally, as things move more to a client-centric model I believe it is best to surface problems and issues with an application within a user exposable medium, such as a SharePoint list. This is possible to do within a SharePoint app in a relatively generic fashion. It makes it really easy to use. Firstly, consider the two following methods which act as the engines for capturing relevant errors:

[csharp]

public TValue ErrorHandler(Func action)
{
try
{
return action();
}
catch (Exception ex)
{
DumpErrorIntoLog(DateTime.Now.ToString(CultureInfo.InvariantCulture), ex.Message);
return default(TValue);
}
}

public void ErrorHandler(Action action)
{
try
{
action();
}
catch (Exception ex)
{
DumpErrorIntoLog(DateTime.Now.ToString(CultureInfo.InvariantCulture), ex.Message);
}
}

[/csharp]

Using the above, handling the error is pretty easy. All you have to do is use the following method in order to harness an app level list, and dump the error in that. First though, to get the context token, web specs and all that crap I am going to use a context utility class.

[csharp]

public class ContextUtility
{
public ContextUtility(HttpRequest request)
{
ContextDetails = new SpContext {ServerUrl = request.Url.Authority, HostWebUrl = HttpContext.Current.Request[“SPHostUrl”], AppWebUrl = HttpContext.Current.Request[“SPAppWebUrl”], ContextTokenString = TokenHelper.GetContextTokenFromRequest(request)};

if (ContextToken == null)
{
try
{
ContextToken = TokenHelper.ReadAndValidateContextToken(ContextDetails.ContextTokenString, ContextDetails.ServerUrl);
}
catch (Exception)
{
ContextToken = null;
}
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
HttpCookie cookie = new HttpCookie(“SPContext”, serializer.Serialize(ContextDetails));
cookie.Expires = DateTime.Now.AddHours(12);
HttpContext.Current.Response.Cookies.Add(cookie);
}

public ContextUtility(SpContext context)
{
ContextDetails = context;
try
{
ContextToken = TokenHelper.ReadAndValidateContextToken(ContextDetails.ContextTokenString, ContextDetails.ServerUrl);
}
catch (Exception)
{
ContextToken = null;
}
}

public ContextUtility()
{
}

public SharePointContextToken ContextToken { get; set; }
public SpContext ContextDetails { get; set; }

public bool IsValid
{
get { return ContextToken != null; }
}

public static ContextUtility Current
{
get
{
ContextUtility spContext = null;
if (HttpContext.Current.Request.Cookies[“SPContext”] != null)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
spContext = new ContextUtility((SpContext) serializer.Deserialize(HttpContext.Current.Request.Cookies[“SPContext”].Value, typeof (SpContext)));
}
if (spContext == null || !spContext.IsValid)
{
spContext = new ContextUtility(HttpContext.Current.Request);
}

if (spContext.IsValid)
{
return spContext;
}
HttpContext.Current.Response.Redirect(GetRedirectUrl());
return null;
}
}

private static string GetRedirectUrl()
{
string hostWebUrl = HttpContext.Current.Request[“SPHostUrl”];
return TokenHelper.GetAppContextTokenRequestUrl(hostWebUrl, HttpContext.Current.Server.UrlEncode(HttpContext.Current.Request.Url.ToString()));
}
}

public class SpContext
{
public string HostWebUrl { get; set; }
public string AppWebUrl { get; set; }
public string ContextTokenString { get; set; }
public string ServerUrl { get; set; }
}

[/csharp]

Then the actual SharePoint communication, and that’s it.

[csharp]

public void DumpErrorIntoLog(string time, string message)
{
ContextUtility utility = new ContextUtility(HttpContext.Current.Request);
using (ClientContext context = TokenHelper.GetClientContextWithContextToken(utility.ContextDetails.AppWebUrl, utility.ContextDetails.ContextTokenString, HttpContext.Current.Request.Url.Authority))
{
List list = context.Web.Lists.GetByTitle(“ErrorsAndShit”);
context.Load(list);
context.ExecuteQuery();
if (list == null)
{
return;
}
CamlQuery query = CamlQuery.CreateAllItemsQuery();
ListItemCollection items = list.GetItems(query);
context.Load(items);
context.ExecuteQuery();
ListItem item = items.First();
item[“Errors”] = string.Format(“Time: {0} Message: {1}”, time, message);
item.Update();
context.ExecuteQuery();
}
}

[/csharp]

Share

Properly Setting A Managed Metadata Field In CSOM

90% of the bullshit you will read on the net will allude to these messed up delimiters being the solution when trying to set a managed metadata field in Office 365. This is definitely not the way to do it.

[csharp]

string valueTouse = String.Format(“-1;#{0}|{1}”, term.Name, term.Id);

[/csharp]

Not only is this a terrible way to do it through shitty string literals, there are strongly typed means to accomplish the same thing that are better supported and slightly more readable. The appropriate approach looks like the following:

[csharp]

Term item = null;
List list = new List();
foreach (Term term in termset.Terms)
{
context.Load(term);
context.ExecuteQuery();
if ([Do some sort of test here maybe?])
{
// don’t do this ass crazy approach
string valueTouse = String.Format(“-1;#{0}|{1}”, term.Name, term.Id);
//rather add to collection
item = term;
}
}
list.Add(item);
}
field.SetFieldValueByCollection(spListItem, new Collection(list), 1033);
spListItem.Update();
context.ExecuteQuery();

[/csharp]

Where in the above, the spListItem variable is a typed ListItem object. Much cleaner and relevant code. You can get the LCID for the SetFieldValueByCollection automagically if you wanted ala reflection or interrogating the relevant SharePoint proxy objects.

Share

Adding Remote Event Receivers To An Exisiting List In Office 365

This is a generic approach in order to add event receivers to an existing list, say at the host or app web level really doesn’t matter. There are a couple of methods that make it nice and clean.

This first part will define a little helper method to build the creation information for the event receiver. Notice you have to specify the remote app url in your manifest tokens.

[csharp]

public static EventReceiverDefinitionCreationInformation BuildGenericEventRecieverWithoutEventType()
{
string remoteAppurl = HttpContext.Current.Request[“RemoteAppUrl”];
string remoteEventEndPointUrl = String.Format(“{0}/PutYourServiceNameHere.svc”, remoteAppurl);
EventReceiverDefinitionCreationInformation eventReceiver = new EventReceiverDefinitionCreationInformation();
eventReceiver.ReceiverName = “GiveYourRevc”;
eventReceiver.ReceiverUrl = remoteEventEndPointUrl;
eventReceiver.SequenceNumber = 5000;
eventReceiver.ReceiverClass = “Fully qualified Path To RER Assembly”;
eventReceiver.ReceiverAssembly = Assembly.GetExecutingAssembly().FullName;
return eventReceiver;
}

[/csharp]

If you are adding or removing a receiver, you of course have to test whether that receiver exists! That’s what the below method does. We just use the class name since that should be unique.

[csharp]

public static bool DoesEventReceiverDefintionExistBasedOnCreationInfo(EventReceiverDefinitionCreationInformation info, List list)
{
bool exists = false;
list.Context.Load(list, x => x.EventReceivers);
list.Context.ExecuteQuery();
foreach (EventReceiverDefinition eventReceiverDefinition in list.EventReceivers)
{
string innerDefId = eventReceiverDefinition.ReceiverClass;
string outerDefId = info.ReceiverClass;
if (innerDefId.Equals(outerDefId, StringComparison.InvariantCultureIgnoreCase))
{
exists = true;
break;
}
}
return exists;
}

[/csharp]

Since the receivers were built without a specific event type, you can specify as many receiver capture behaviors as you want, as demonstrated in the below example.

[csharp]

public static void ApplyEventReceiver(ClientContext context, List list)
{
EventReceiverDefinitionCreationInformation eventReceiver = Helpers.BuildGenericEventRecieverWithoutEventType();
eventReceiver.EventType = EventReceiverType.ItemAdded;
if (!DoesEventReceiverDefintionExistBasedOnCreationInfo(eventReceiver, list))
{
list.EventReceivers.Add(eventReceiver);
}
eventReceiver = BuildGenericEventRecieverWithoutEventType();
eventReceiver.EventType = EventReceiverType.ItemUpdated;
if (!DoesEventReceiverDefintionExistBasedOnCreationInfo(eventReceiver, list))
{
list.EventReceivers.Add(eventReceiver);
}
eventReceiver = BuildGenericEventRecieverWithoutEventType();
eventReceiver.EventType = EventReceiverType.ItemDeleted;
if (!DoesEventReceiverDefintionExistBasedOnCreationInfo(eventReceiver, list))
{
list.EventReceivers.Add(eventReceiver);
}
eventReceiver = BuildGenericEventRecieverWithoutEventType();
eventReceiver.EventType = EventReceiverType.ItemFileMoved;
if (!DoesEventReceiverDefintionExistBasedOnCreationInfo(eventReceiver, list))
{
list.EventReceivers.Add(eventReceiver);
}
context.ExecuteQuery();
}

[/csharp]

Unapplying event receivers is pretty easy to, but remember you are modifying the collection so you have to ensure that the enumeration can execute! That is why you see the two LINQ chains.

[csharp]

public static void UnApplyEventReceiver(ClientContext context, List list)
{
EventReceiverDefinitionCollection eventReceiverDefinitionCollection = list.EventReceivers;
context.Load(eventReceiverDefinitionCollection);
context.ExecuteQuery();
List ids = eventReceiverDefinitionCollection.Select(eventReceiverDefinition => eventReceiverDefinition.ReceiverId).ToList();
foreach (EventReceiverDefinition definition in ids.Select(eventReceiverDefinitionCollection.GetById))
{
definition.DeleteObject();
context.ExecuteQuery();
}
}

[/csharp]

Share