A quick word on organizing extension methods: I usually collect them in an Extensions folder, appending Extensions to the name of class being extended and keeping with the one class per file convention. For brevity I’ve left out the using and the namespace part below.
In SharePoint 2010 the TryGetList method has been added to the SPListCollection class. The method returns either an SPList instance matching the display name or null. Oftentimes, however, you want to do a lookup based on the internal name. Here’s an extension method that adheres to the semantics of TryGetList, but using the internal name. It relies on the fact that the RootFolder property of a list is actually its internal name:
// definition public static class SPListCollectionExtensions { public static SPList TryGetListByInternalName(this SPListCollection lists, string internalName) { return (from SPList l in lists where l.RootFolder.Name == internalName select l).SingleOrDefault(); } } // use if (site.Lists.TryGetListByInternalName(internalListName) == null) // list not found
Using the CreateNewField method of the SPFieldCollection you can add new fields to a list. The particular annoying aspect of this method, however, is that when you want to continue working with its result, oftentimes you have to cast it to one of the SPField subclasses. But since the SPFieldType, provided as one of the arguments to CreateNewField, closely relates to the actual SPField return type, an extension method is able to do the casting. This’ll expose mismatches at compile time instead of at runtime.
All it takes is for us to map out the relation between SPField and SPFieldType:
// definition public static class SPFieldCollectionExtensions { public static TSPField CreateField<TSPField>(this SPFieldCollection fields, string internalName, string displayName) where TSPField : SPField { var spFieldToFieldType = new Dictionary<Type, SPFieldType> { { typeof(SPFieldDateTime), SPFieldType.DateTime }, { typeof(SPFieldNumber), SPFieldType.Number }, { typeof(SPFieldUser), SPFieldType.User }, { typeof(SPFieldBoolean), SPFieldType.Boolean }, { typeof(SPFieldMultiLineText), SPFieldType.Note }, { typeof(SPFieldText), SPFieldType.Text } }; var fieldType = spFieldToFieldType[typeof(TSPField)]; var list = fields.List; var field = list.Fields[list.Fields.Add(internalName, fieldType, false)]; field.Title = displayName; field.Update(); return field as TSPField; } } // use l.Fields.CreateField<SPFieldBoolean>(internalName, "displayName");
Taking the CreateField extension method one step further, oftentimes you want to set properties besides internal name and display name. For that purpose I’ve defined a CreateField method that accepts an Action<TField>. This allows you to reuse common property settings across fields for brevity and consistency while maintaining strong typing.
// definition public static TSPField CreateField<TSPField>(this SPFieldCollection fields, string internalName, string displayName, Action<TSPField> setAdditionalProperties) where TSPField : SPField { var newField = CreateField<TSPField>(fields, internalName, displayName); setAdditionalProperties(newField); newField.Update(); return newField; } // use public static Action<SPFieldMultiLineText> RichTextProperties = f => { f.RichText = true; f.RichTextMode = SPRichTextMode.FullHtml; }; l.Fields.CreateField<SPFieldBoolean>(internalName, "displayName", f => f.Required = true); l.Fields.CreateField(internalName, "displayName", RichTextProperties);
With the Comment field, you can leave out the type argument because the compiler infers it based on the type of the Action delegate.
Similar to CreateField, I’ve defined two additional extension methods for creating lookup fields:
// definition public static TSPField CreateLookup<TSPField>(this SPFieldCollection fields, string lookupListName, string internalName, string displayName) where TSPField : SPFieldLookup { var currentList = fields.List; var lookupList = currentList.ParentWeb.Lists.TryGetListByInternalName(lookupListName); var newField = currentList.Fields[currentList.Fields.AddLookup(internalName, lookupList.ID, false)]; newField.Title = displayName; newField.Update(); return newField as TSPField; } public static TSPField CreateLookup<TSPField>(this SPFieldCollection fields, string lookupListName, string internalName, string displayName, Action<TSPField> setAdditionalProperties) where TSPField : SPFieldLookup { var newField = CreateLookup<TSPField>(fields, lookupListName, internalName, displayName); setAdditionalProperties(newField); newField.Update(); return newField; } // use l.Fields.CreateLookup<SPFieldLookup>(lookupListName, internalName, displayName, f => f.AllowMultipleValues = true);
These extension methods makes using the SharePoint API more type-safe and concise, and defining lists using these methods and the template approach saves me from writing a lot of repetitive code.