SharePoint Design Patterns: Entry 2
In the previous entry, we explored how to clean up our interaction with instances of SPSite, SPWeb, and SPList objects. Logically, the next scenario that we’d want to cover is working with the SPListItem that we get back from the list.
What does this look like? Usually something like this (we’ll use some sample code from our last entry):
1 2 3 4 5 6 7 8 9 |
<span style="font-family: Lucida Console;"><span style="color: #0000ff;">public string </span><span style="color: #000000;">GetLastModifiedBy(<span style="color: #0000ff;">int</span> id) {</span> <span style="color: #0000ff;">string </span><span style="color: #000000;">lastModifiedBy;</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">using</span><span style="color: #000000;">(ProposalsLibrary library = </span><span style="color: #0000ff;">new </span><span style="color: #000000;">ProposalsLibrary()) {</span> <span style="color: #000000;">SPListItem item = ...; </span><span style="color: #008000;">// Get the instance</span></span> <span style="font-family: Lucida Console;"> <span style="color: #000000;">lastModifiedBy = Convert.ToString(item[</span><span style="color: #ff00ff;">"Modified_x0020_By"</span><span style="color: #000000;">]);</span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">return </span><span style="color: #000000;">lastModifiedBy;</span> <span style="color: #000000;">}</span></span> |
By itself, this seems innocent enough, but the problem lies in the fact that it’s rarely so easy as returning simply one field value. Even if that were the case, the issue remains that these field name strings leak out across the codebase. Again, this is typically mitigated through the use of Constants or configuration settings, but it’s still messy code, IMO. Aside from that, it can be difficult to figure out which fields are valid for which lists. In general, it makes it hard for developers to figure out how to work with the existing code without good documentation.
Enter the Decorator pattern. The intent of Decorator, as described in Design Patterns, is as follows:
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality.
Basically, what we’d like to do is to make working with SPListItems more intuitive and more domain specific. Since we can’t subclass SPListItem (and even if we could I don’t think it would make sense to do so since it would also expose all of the properties and operations on the SPListItem), we’ll have to leverage the simplified version of the Decorator pattern to help us instead. Here is an example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
<span style="font-family: Lucida Console;"><span style="color: #0000ff;">using </span><span style="color: #008080;">System</span><span style="color: #000000;">;</span> <span style="color: #0000ff;">using </span><span style="color: #008080;">Microsoft</span><span style="color: #000000;">.SharePoint;</span></span> <span style="font-family: Lucida Console;"><span style="color: #0000ff;">namespace </span><span style="color: #000000;">SharePointExample</span> <span style="color: #000000;">{</span> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Models a sales proposal.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #0000ff;">public class </span><span style="color: #000000;">Proposal</span> <span style="color: #000000;">{</span> <span style="color: #0000ff;">private </span><span style="color: #000000;">SPListItem _managedItem;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_title;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_lastModifiedBy;</span> <span style="color: #0000ff;">private bool </span><span style="color: #000000;">_checkedOut;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_checkedOutUserLoginName;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_checkedOutUserDisplayName;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_status;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_customerName;</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Gets the title.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #008000;">/// <value>The title.</value></span> <span style="color: #0000ff;">public string </span><span style="color: #000000;">Title</span> <span style="color: #000000;">{</span> <span style="color: #000000;">get { </span><span style="color: #0000ff;">return </span><span style="color: #000000;">_title; }</span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Gets the login name of the check out user.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #008000;">/// <remarks></span> <span style="color: #008000;">/// Null or empty if not checked out.</span> <span style="color: #008000;">/// </remarks></span> <span style="color: #008000;">/// <value>The login name of the checkout user.</value></span> <span style="color: #0000ff;">public string </span><span style="color: #000000;">CheckedOutUserLoginName</span> <span style="color: #000000;">{</span> <span style="color: #000000;">get { </span><span style="color: #0000ff;">return </span><span style="color: #000000;">_checkedOutUserLoginName; }</span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">// Other properties omitted...</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Private constructor; use the <see cref="FromSPListItem"/></span> <span style="color: #008000;">/// call to create an instance.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #0000ff;">private </span><span style="color: #000000;">Proposal() { }</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Creates an instance from a SharePoint list item.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #008000;">/// <param name="item">The item.</param></span> <span style="color: #008000;">/// <returns>An instance of <c>Proposal</c>.</returns></span> <span style="color: #0000ff;">public static </span><span style="color: #000000;">Proposal FromSPListItem(SPListItem item)</span> <span style="color: #000000;">{</span> <span style="color: #000000;">Proposal proposal = </span><span style="color: #0000ff;">new </span><span style="color: #000000;">Proposal();</span></span> <span style="font-family: Lucida Console;"> <span style="color: #000000;">proposal._title = </span><span style="color: #808000;">Convert</span><span style="color: #000000;">.ToString(item[</span><span style="color: #ff00ff;">"Title"</span><span style="color: #000000;">]);</span> <span style="color: #000000;">proposal._checkedOut = (item[</span><span style="color: #ff00ff;">"CheckoutUser"</span><span style="color: #000000;">] != </span><span style="color: #0000ff;">null</span> <span style="color: #000000;">&& item.</span><span style="color: #808000;">File</span><span style="color: #000000;">.CheckOutStatus != SPFile.SPCheckOutStatus.None);</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">if </span><span style="color: #000000;">(proposal._checkedOut) {</span> <span style="color: #0000ff;">string </span><span style="color: #000000;">fieldValue = </span><span style="color: #808000;">Convert</span><span style="color: #000000;">.ToString(item[</span><span style="color: #ff00ff;">"CheckoutUser"</span><span style="color: #000000;">]);</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">// NOTE: item.Fields requires the display name.</span> <span style="color: #000000;">SPFieldUserValue checkoutUser = (SPFieldUserValue)</span> <span style="color: #000000;">item.Fields[</span><span style="color: #ff00ff;">"Checked Out To"</span><span style="color: #000000;">].GetFieldValue(fieldValue);</span></span> <span style="font-family: Lucida Console;"> <span style="color: #000000;">proposal._checkedOutUserLoginName = checkoutUser.User.</span><span style="color: #808000;">LoginName</span><span style="color: #000000;">;</span> <span style="color: #000000;">proposal._checkedOutUserDisplayName = checkoutUser.User.Name;</span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">// Set other fields here...</span></span> <span style="font-family: Lucida Console;"> <span style="color: #000000;">proposal._managedItem = item;</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">return </span><span style="color: #000000;">proposal;</span> <span style="color: #000000;">}</span> <span style="color: #000000;">}</span> <span style="color: #000000;">}</span></span> |
If nothing else, we now have a clean, domain specific way to access the SharePoint list item. Team members and new developers don’t have to guess which fields are valid for this items retrieved from this list; it would be hidden from them by the properties on the Proposal class instead. If you look at the code to determine the check-out user, you can see that we now also have a single location to encapsulate this parsing logic and any associated error handling we may want to add; other developers don’t need to duplicate this code when they use the list item.
It’s debatable whether you should use a constructor to initialize the instance, some sort of implicit conversion operation, or a static method like I’ve used here. I would rule out an implicit conversion operator since it may be hard for users to understand at first blush (i.e. XName and string). As for a “natural” constructor that takes the SPListItem instance? While it’s slightly more discoverable (XDocument.Parse() is pretty hard to find for new users), I feel like that’s a bit misleading and the intent isn’t clear, however, YMMV. Another possible improvement is to pull the abstraction up yet another level since many of the fields are common (like title and last modified by); this exercise is left to the reader ;-).
If we go back to our first example, we can now write this as:
1 2 3 4 5 6 7 8 9 10 11 12 |
<span style="font-family: Lucida Console;"><span style="color: #0000ff;">public string </span><span style="color: #000000;">GetLastModifiedBy(<span style="color: #0000ff;">int</span> id) {</span> <span style="color: #0000ff;">string </span><span style="color: #000000;">lastModifiedBy;</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">using</span><span style="color: #000000;">(ProposalsLibrary library = </span><span style="color: #0000ff;">new </span><span style="color: #000000;">ProposalsLibrary()) {</span> <span style="color: #000000;">SPListItem item = ...; </span><span style="color: #008000;">// Get the instance</span></span> <span style="font-family: Lucida Console;"> <span style="color: #000000;"><strong>lastModifiedBy = Proposal.FromSPListItem(item)</strong><strong>.LastModifiedBy;</strong></span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">return </span><span style="color: #000000;">lastModifiedBy;</span> <span style="color: #000000;">}</span></span> |
That’s pretty cool. While this example is intentionally simple, you now have a nice object oriented way of working the the fields. In addition, you have a very logical place to put all of your domain specific operations on specific list item types (or should that be content types?). For example, it may make perfect sense to add a method here called CompressAndEmail(string recipientEmailAddress) which would compress the file contents of the proposal and email it to the specified recipient.
Admittedly, this still isn’t ideal since 1) we still have to deal with SPListItem on some level (“leakage”, if you will) and 2) what about the other side of this equation: updating the item? With regards to (1), we can simply hide this by adding a method to the ProposalsLibrary class as discussed in the previous entry:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<span style="font-family: Lucida Console;"><span style="color: #0000ff;">using </span><span style="color: #008080;">Microsoft</span><span style="color: #000000;">.SharePoint;</span></span> <span style="font-family: Lucida Console;"><span style="color: #0000ff;">namespace </span><span style="color: #000000;">SharePointExample</span> <span style="color: #000000;">{</span> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Concrete implementation of <see cref="Library"/></span> <span style="color: #008000;">/// </summary></span> <span style="color: #0000ff;">public class </span><span style="color: #000000;">ProposalsLibrary : Library</span> <span style="color: #000000;">{</span> <span style="color: #0000ff;">protected override string </span><span style="color: #000000;">ListName</span> <span style="color: #000000;">{</span> <span style="color: #000000;">get { </span><span style="color: #0000ff;">return </span><span style="color: #ff00ff;">"<listNameHere>"</span><span style="color: #000000;">; }</span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">protected override string </span><span style="color: #000000;">WebName</span> <span style="color: #000000;">{</span> <span style="color: #000000;">get { </span><span style="color: #0000ff;">return </span><span style="color: #ff00ff;">"<webNameHere>"</span><span style="color: #000000;">; }</span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Finds and instance of <see cref="Proposal"/> the by ID.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #008000;">/// <param name="id">The ID.</param></span> <span style="color: #008000;">/// <returns>An instance of <see cref="Proposal"/>.</returns></span> <strong><span style="color: #0000ff;">public </span><span style="color: #000000;">Proposal FindById(</span><span style="color: #0000ff;">int </span><span style="color: #000000;">id)</span></strong> <span style="color: #000000;">{</span> <span style="color: #000000;">SPListItem item = ...; </span><span style="color: #008000;">// Find the list item</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">return </span><span style="color: #000000;">Proposal.FromSPListItem(item);</span> <span style="color: #000000;">}</span> <span style="color: #000000;">}</span> <span style="color: #000000;">}</span></span> |
Now our method looks like this instead:
1 2 3 4 5 6 7 8 9 10 11 12 |
<span style="font-family: Lucida Console;"><span style="color: #0000ff;">public string </span><span style="color: #000000;">GetLastModifiedBy(</span><span style="color: #0000ff;">int </span><span style="color: #000000;">id) {</span> <span style="color: #0000ff;">string </span><span style="color: #000000;">lastModifiedBy;</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">using</span><span style="color: #000000;">(ProposalsLibrary library = </span><span style="color: #0000ff;">new </span><span style="color: #000000;">ProposalsLibrary()) {</span> <strong><span style="color: #000000;">Proposal proposal = library.FindById(id);</span></strong></span> <span style="font-family: Lucida Console;"><strong> <span style="color: #000000;">lastModifiedBy = proposal.LastModifiedBy;</span></strong> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #0000ff;">return </span><span style="color: #000000;">lastModifiedBy;</span> <span style="color: #000000;">}</span></span> |
That’s pretty awesome since it means that we can now remove all references to and knowledge of SharePoint from a whole layer of our application code.
Now onto point (2). For a moment, let’s say our proposals have properties called “Status” and “CustomerName”. From time to time, we may want to update these (from a source other than the web form, like through a custom web service call or if we have a batch process that runs on our SharePoint environment). One way we can handle this is by implementing the set operation on the properties which can be updated and a Save() method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
<span style="font-family: Lucida Console;"><span style="color: #0000ff;">using </span><span style="color: #008080;">System</span><span style="color: #000000;">;</span> <span style="color: #0000ff;">using </span><span style="color: #008080;">Microsoft</span><span style="color: #000000;">.SharePoint;</span></span> <span style="font-family: Lucida Console;"><span style="color: #0000ff;">namespace </span><span style="color: #000000;">SharePointExample</span> <span style="color: #000000;">{</span> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Models a sales proposal.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #0000ff;">public class </span><span style="color: #000000;">Proposal</span> <span style="color: #000000;">{</span> <span style="color: #0000ff;">private </span><span style="color: #000000;">SPListItem _managedItem;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_title;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_lastModifiedBy;</span> <span style="color: #0000ff;">private bool </span><span style="color: #000000;">_checkedOut;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_checkedOutUserLoginName;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_checkedOutUserDisplayName;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_status;</span> <span style="color: #0000ff;">private string </span><span style="color: #000000;">_customerName;</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Gets or sets the status.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #008000;">/// <value>The status.</value></span> <span style="color: #0000ff;">public string </span><span style="color: #000000;">Status</span> <span style="color: #000000;">{</span> <span style="color: #000000;">get { </span><span style="color: #0000ff;">return </span><span style="color: #000000;">_status; }</span> <span style="color: #000000;"><strong>set { _status = value; }</strong></span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Gets or sets the name of the customer.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #008000;">/// <value>The name of the customer.</value></span> <span style="color: #0000ff;">public string </span><span style="color: #000000;">CustomerName</span> <span style="color: #000000;">{</span> <span style="color: #000000;">get { </span><span style="color: #0000ff;">return </span><span style="color: #000000;">_customerName; }</span> <span style="color: #000000;"><strong>set { _customerName = value; }</strong></span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">// Other properties omitted...</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Private constructor; use the <see cref="FromSPListItem"/></span> <span style="color: #008000;">/// call to create an instance.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #0000ff;">private </span><span style="color: #000000;">Proposal() { }</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Saves this instance back to SharePoint.</span> <span style="color: #008000;">/// </summary></span> <strong><span style="color: #0000ff;">public void </span><span style="color: #000000;">Save()</span></strong> <span style="color: #000000;">{</span> <span style="color: #008000;">// Error checking first; i.e check if user has item checked out</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">// Save</span> <span style="color: #000000;">_managedItem[</span><span style="color: #ff00ff;">"Customer_x0020_Name"</span><span style="color: #000000;">] = CustomerName;</span> <span style="color: #000000;">_managedItem[</span><span style="color: #ff00ff;">"Status"</span><span style="color: #000000;">] = Status;</span> <span style="color: #000000;">_managedItem.SystemUpdate(</span><span style="color: #0000ff;">false</span><span style="color: #000000;">);</span> <span style="color: #000000;">}</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">/// <summary></span> <span style="color: #008000;">/// Creates an instance from a SharePoint list item.</span> <span style="color: #008000;">/// </summary></span> <span style="color: #008000;">/// <param name="item">The item.</param></span> <span style="color: #008000;">/// <returns>An instance of <c>Proposal</c>.</returns></span> <span style="color: #0000ff;">public static </span><span style="color: #000000;">Proposal FromSPListItem(SPListItem item)</span> <span style="color: #000000;">{</span> <span style="color: #008000;">// Same as before; omitted</span> <span style="color: #000000;">}</span> <span style="color: #000000;">}</span> <span style="color: #000000;">}</span></span> |
Nice. In doing this, we’ve basically hidden most knowledge of the SharePoint list item from the users of our object and framework. Downstream developers only need to know about the domain specific objects. It’s now far easier for a member of our team or a new developer to reuse this code and it cuts down on error prone duplication and leakage of field names across our codebase. We also now have a nice place to put field validation logic (for example, in the Save() method). If we wanted to, we can also add logic here (through some references to services or DAOs or something) to load additional metadata from external systems like databases.
If you want to get fancier, you could also add a dictionary keyed by string (internal field name) and with value type object and use that to hold your “set” properties until Save() is called. In the save, you’d simply iterate the keys and set the values then update, only changing the values that were actually set by the caller.
Updating the status of a proposal would go from looking like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<span style="font-family: Lucida Console;"><span style="color: #0000ff;">public void </span><span style="color: #000000;">UpdateStatus(</span><span style="color: #0000ff;">int </span><span style="color: #000000;">id, </span><span style="color: #0000ff;">string </span><span style="color: #000000;">status) {</span> <span style="color: #0000ff;">using</span><span style="color: #000000;">(SPSite site = </span><span style="color: #0000ff;">new </span><span style="color: #000000;">SPSite(_siteUrl)) {</span> <span style="color: #0000ff;">using</span><span style="color: #000000;">(SPWeb web = site.OpenWeb(_web)) {</span> <span style="color: #000000;">SPList list = web.Lists[_list];</span></span> <span style="font-family: Lucida Console;"> <span style="color: #000000;">SPListItem item = ...; </span><span style="color: #008000;">// Get the list item</span></span> <span style="font-family: Lucida Console;"> <span style="color: #008000;">// Perhaps add error checking.</span></span> <span style="font-family: Lucida Console;"> <span style="color: #000000;">item[</span><span style="color: #ff00ff;">"Status"</span><span style="color: #000000;">] = status;</span></span> <span style="font-family: Lucida Console;"> <span style="color: #000000;">item.SystemUpdate();</span> <span style="color: #000000;">}</span> <span style="color: #000000;">}</span> <span style="color: #000000;">}</span></span> |
To this instead:
1 2 3 4 5 6 7 8 9 |
<span style="font-family: Lucida Console;"><span style="color: #0000ff;">public void </span><span style="color: #000000;">UpdateStatus(</span><span style="color: #0000ff;">int </span><span style="color: #000000;">id, </span><span style="color: #0000ff;">string </span><span style="color: #000000;">status) {</span> <span style="color: #0000ff;">using</span><span style="color: #000000;">(ProposalsLibrary library = </span><span style="color: #0000ff;">new </span><span style="color: #000000;">ProposalsLibrary()) {</span> <span style="color: #000000;">Proposal proposal = library.FindById(id);</span></span> <span style="font-family: Lucida Console;"> <span style="color: #000000;">proposal.Status = status;</span> <span style="color: #000000;">proposal.Save();</span> <span style="color: #000000;">}</span> <span style="color: #000000;">}</span></span> |
It becomes even more compelling once we consider that in a real world implementation of an UpdateStatus() operation, it would probably involve checking to see if a user has the object checked out in the first place! This would make our first example explode into a giant mess of code while in our second one, it would be one line (or integrated into the Save() method which could throw a custom NotCheckedOutException or framework InvalidOperationException or try to check it out automatically).
All this with relatively little work involved. It’s better in every way: less nesting of code, much more readable and natural, less error prone, better discoverability, and more domain specific (and less SharePoint-centric). In the end, I think it dramatically improves usability and reuse of your SharePoint application code. It also leads to a nice, logical place to encapsulate much of the common, repetitive, and error prone code that would otherwise be littered among your application (or worse, UI) code.
In future installments, we’ll examine how flesh out the FindById() method on the ProposalsLibrary. We’ll also examine a GUI pattern to help promote reuse, improve tesatability, and cut down on duplication of business logic.
You may be interested to look at a similar open source project to abstract away SharePoint to allow better concentration of domain-specific coding: code.google.com/p/domaindrivensharepoint/ – not complete I think, but it’s going along the same path as you.
Peter,
Sounds awesome. The more that developers approach the SharePoint Issue in a quasi-domain driven way, the better!
— Chuck
item["Title"] – bad practice for built-in fields, see at this dict: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spbuiltinfieldid_fields.aspx
And more SharePoint practices you will read in http://spg.codeplex.com/
Vitaly,
That’s a good catch. Although in the case of "Title", it seems like it would be pretty consistent.
The built in fields are definitely handy to mitigate discovery of some of the more obscure fields in SharePoint.
Also, another good link 🙂 I think the value is in the documentation and code samples. We need more of these to balance out the tons of quick-and-dirty examples on the web.