Using Silverlight and Web Services for SharePoint Server 2010 Data Access (Wrox)

Summary: Learn how to use Microsoft SharePoint 2010 web services to access and manage data from Microsoft Silverlight. Specifically, learn how to use Silverlight with SharePoint web services to retrieve and update list items and to upload list attachments.

Wrox logo

Wrox SharePoint Books

Applies to: Business Connectivity Services | Open XML | SharePoint | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Author: Raul Rodila

Editors: WROX Tech Editors for SharePoint 2010 Articles

Contents

  • Introduction to Using Silverlight and Web Services for SharePoint Server 2010 Data Access

  • Introduction to SharePoint Web Services

  • When to Use Web Services in Place of, or in Addition to, Other APIs

  • Common Functionality and Where to Find It in the Web Services

  • Making Asynchronous Calls to Web Services from Silverlight

  • Selecting and Loading List Data from the Lists.asmx Service

  • Translating List Data XML to Objects

  • Submitting Changes to Data Through the Web Services

  • Adding an Attachment to a SharePoint List Item

  • About the Author

  • Additional Resources

You can download a complete working version of the code described in this article. The ReadMe.txt file in the download explains how to set up the code. To run the code successfully, you must have access to a SharePoint 2010 site and know how to set up Microsoft Visual Studio 2010 and Silverlight development environments. The source code contains functionality that simplifies working with XML and Collaborative Application Markup Language (CAML). In addition, the code follows the Model View ViewModel pattern, which uses the data binding strengths of Silverlight.

Download the code

Introduction to Using Silverlight and Web Services for SharePoint Server 2010 Data Access

This article uses the following scenario to explain how to use SharePoint 2010 web services to access and manage data from Microsoft Silverlight.

The Adventure Works marketing department managed its assets through a SharePoint team site. The team decided to improve the user experience and provide a rich user interface. Now, they use Silverlight to browse and administer approved graphical assets for their products. Marketing department members use SharePoint workflows and a tasks list to create requests for new assets from the graphic design team. After the graphic design team submits the asset, the contents of the asset are reviewed first by the marketing team, and then by the legal team before the assets are made available to the broader company as approved versions. A simple sequential workflow built in Visual Studio marks the asset as approved after the legal and marketing approval dates are set. The example in this article has a view and edit mode for assets that are in the assets list, and uses the SharePoint WebServices API to manage the items in this list.

Introduction to SharePoint Web Services

SharePoint Web services provide an API that you can use to access SharePoint installations remotely. It has an extensive set of functionality that spans everything from the creation and administration of SharePoint sites, to accessing and managing site data. The API is implemented by using ASMX web services.

The SharePoint Web Services API is different from the familiar server object model that Web Part developers use. While CAML is used extensively in the Web Services API, familiar objects like SPSite and SPList are not present. The developer must know which functionality is available in which service. For example, you can use the Copy service to create a document in a document library and set field values for those documents. However, if a multi-value lookup field is present, it cannot be set by using the Copy service. The Lists service must be invoked to update the value of such a field.

When to Use Web Services in Place of, or in Addition to, Other APIs

SharePoint 2010 introduced two new ways to access and modify SharePoint information from a remote client, the client object model and WCF Data Services, implemented as Representational State Transfer services (REST). The client object model brings a familiar programming technique to the client for developers who have created SharePoint server-side applications. You can use the REST services to work with list data on those platforms where the client object model is not available, such as Windows Phone 7. Both of these SharePoint access methods are available in Silverlight, and they are simpler to work with, so you might wonder why you should ever use SharePoint web services in SharePoint 2010 at all.

The two main reasons why are as follows:

  • To perform operations on SharePoint data that are unavailable through either the client object model or the REST services APIs.

  • To develop applications that work in SharePoint 2010, but that also work in older versions of SharePoint, in which web services provided the sole way to program SharePoint remotely.

Developers might also use web services in legacy SharePoint applications that they want to upgrade, and where rewriting by using one of the new APIs is not practical.

For more information about when to use web services, see Deciding Which SharePoint API to Use.

Common Functionality and Where to Find It in the Web Services

SharePoint Web Services has an extensive set of functionality that you can use to administer and manipulate SharePoint data. There are 25 services available in Microsoft SharePoint Foundation 2010 and there are an additional 6 web services that are specific to the full version of Microsoft SharePoint Server 2010.

The service that is most often used to work with list data and document libraries is the Lists.asmx service. You can use it to retrieve and update list items and to set field values for items in document libraries. Table 1 describes some of the methods.

Table 1.

Method

Description

GetListCollection

Retrieves a list of the lists in the SharePoint site.

GetListItems

Retrieves items from the specified list using a CAML query.

UpdateListItems

Updates or creates one or more list items.

Use the Copy.asmx service to copy files in SharePoint and to upload files to a documents library. This service provides the method described in Table 2.

Table 2.

Method

Description

CopyIntoItems

Copies a document that is represented by a Byte array to one or more locations on a server.

Use the UserGroup.asmx to work with users and groups. This service provides the methods described in Table 3.

Table 3.

Method

Description

GetGroupCollectionFromUser

Retrieves the groups that the specified user belongs to as a member.

GetUserCollectionFromGroup

Retrieves the users who are members of the specified group.

Use the Search.asmx service to access SharePoint indexes. This service provides the method described in Table 4.

Table 4.

Method

Description

Query

Runs a server-side query by using the SharePoint search service.

For a full list of services, see SharePoint 2010 Web Services.

Making Asynchronous Calls to Web Services from Silverlight

Even though Silverlight is associated with web development, programming in Silverlight resembles writing a Windows application more than it resembles writing an ASP.NET application. However, unlike applications that are written by using the full version of .NET, all calls that are made to a server must be asynchronous. For a particular web service, calling a method involves creating the service proxy, registering a call-back handler, and then making the method call asynchronously. With the client object model, Microsoft provides Silverlight DLLs to encapsulate access to SharePoint. Similar DLLs are not available for SharePoint web services. Instead, the developer must add a service reference to the ASMX file in the Silverlight application, which generates the necessary proxy classes.

void LoadProductListData()
{ 
// Create the service proxy
ListsSoapClient service = new ListsSoapClient();

// Register the call-back handler
service.GetListItemsCompleted += new EventHandler<GetListItemsCompletedEventArgs>(
ListsService_GetListItemsCompleted);

// Begin the method call
service.GetListItemsAsync(ListName, viewName, query, viewFields,
rowLimit, queryOptions, string.Empty, null);
}

Add the corresponding call-back handler to perform error checking and handle the result.

void ListsService_GetListItemsCompleted(object sender, GetListItemsCompletedEventArgs e)
{
if (e.Error != null)
HandleException(e.Error);
else
HandleResult(e.Result);
}

As a best practice, the call-back handler should inspect the Error property before accessing the Result property. Otherwise, accessing the Result property could throw an exception, which would mask the real error.

Additionally, if the code accesses any user interface objects or objects that are bound to user interface objects during the time that it processes the results, the code must use the Dispatcher object to invoke the result handling on the user interface thread.

Dispatcher.BeginInvoke(() => HandleResult(e.Result));

Selecting and Loading List Data from the Lists.asmx Service

When you work with SharePoint Lists service, the first step is to add a service reference to Lists.asmx. The Lists service, and every other SharePoint web service, is located in the _vti_bin folder of a SharePoint site (for example, http://sl2sp.local/\_vti\_bin/Lists.asmx).

Use the GetListItems method to retrieve list items. The data is returned as XML.

<listitems xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema" xmlns="https://schemas.microsoft.com/sharepoint/soap/">
<rs:data ItemCount="2">
   <z:row ows_Attachments="1" ows_LinkTitle="LL Bottom Bracket" ows_ProductNumber="BB-7421" ows_ProductCategory="1;#Bottom Brackets" ows_ProductModel="LL Bottom Bracket" ows_Description="Chromoly steel." ows_AttachmentFile="/Lists/Products/Attachments/1/Tulips.jpg" ows__ModerationStatus="0" ows__Level="1" ows_Title="LL Bottom Bracket" ows_ID="1" ows_UniqueId="1;#{D7425118-089A-40B7-98CD-EA3968E84EC6}" ows_owshiddenversion="32" ows_FSObjType="1;#0" ows_Created_x0020_Date="1;#2011-04-14 10:35:58" ows_Created="2011-04-14 10:35:58" ows_FileLeafRef="1;#1_.000" ows_PermMask="0x7fffffffffffffff" ows_Modified="2011-05-26 00:21:22" ows_FileRef="1;#Lists/Products/1_.000" ows_MetaInfo="1;#" />
   <z:row ows_Attachments="1" ows_LinkTitle="ML Bottom Bracket" ows_ProductNumber="BB-8107" ows_ProductCategory="1;#Bottom Brackets" ows_ProductModel="ML Bottom Bracket" ows_Description="Aluminum alloy cups; large diameter spindle." ows_AttachmentFile="/Lists/Products/Attachments/2/Penguins.jpg" ows__ModerationStatus="0" ows__Level="1" ows_Title="ML Bottom Bracket" ows_ID="2" ows_UniqueId="2;#{39915255-4B04-43F5-AC66-E7385D8EBB21}" ows_owshiddenversion="3" ows_FSObjType="2;#0" ows_Created_x0020_Date="2;#2011-04-14 10:35:58" ows_Created="2011-04-14 10:35:58" ows_FileLeafRef="2;#2_.000" ows_PermMask="0x7fffffffffffffff" ows_Modified="2011-05-26 00:22:18" ows_FileRef="2;#Lists/Products/2_.000" ows_MetaInfo="2;#" />
</rs:data>
</listitems>

In the preceding XML, each row represents a record in a SharePoint list. Although you could use the XML directly, to make it useful in Silverlight, you should convert it into a collection of objects.

The following are a few interesting facts to note about the returned XML:

  • Values for lookup columns are returned as a delimited string of ID and value, using ";#" as the delimiter, for example, ows_ProductCategory="1;#Bottom Brackets".

  • The value before the delimiter is the ID of the SharePoint list item. The value after the delimiter is the content of the field that was selected as the display field on the lookup column from the referenced list. Although not present in this example, the value for multi-value lookup columns is separated with ";#" between each ID/value pair, which in turn uses ";#" to separate the ID from the value.

  • All field names are prefixed with "ows_", and any field name that has a space contains "_x0020_" in the place of the space.

  • A fact that is not obvious and can be difficult to figure out is the following: the name that is used to represent a field is the underlying name of a SharePoint list column, not the display name. If a column named "ProdCtg" is created in a list and then later renamed, "Product Category," you might expect to see a field name like "ows_Product_x0020_Category". Instead, the XML contains "ows_ProdCtg". One way to find the correct names for all columns of a list is to write a simple CAML query (for example, <Query xmlns="https://schemas.microsoft.com/sharepoint/soap/"></Query>), run it against the list in question, and inspect the XML result to see the exact column names that the SharePoint web services use. To quickly find the name for one column, use a web browser to navigate to the list that contains the column, and then click the column name in the List Settings. The URL contains the real column name in the form, "...&Field=RealColumnName".

  • The fields returned by the GetListItems method are those in the Default View of the SharePoint list. You can specify a different view in the service call and a different sort order for the results. The call allows for additional parameters to be specified, which provide query refinements such as recursively traversing folders when retrieving items.

Translating List Data XML to Objects

As noted earlier, the SharePoint Web Services returns list data in the form of XML. Silverlight uses an advanced binding model that works best with objects and properties, not XML. The developer must convert the XML from the Lists service into a set of equivalent objects.

To simplify the conversion of XML into objects, you can build a set of base classes to encapsulate common functionality that is available to any SharePoint list. The following ListItemBase class contains two properties that are common to all SharePoint list items, the ID and Title columns.

public class ListItemBase : ObservableObjectBase
{
public int Id 
{
get { return _id; }
set
{
if ((this._id != value))
{
this._id = value;
NotifyPropertyChanged("Id");
}
}
}
private int _id;

public string Title
{
get { return _title; }
set
{
if ((this._title != value))
{
this._title = value;
NotifyPropertyChanged("Title");
}
}
}
protected string _title;
}

Note that the ListItemBase class inherits from the ObservableObjectBase class. The ObservableObjectBase class provides the implementation for the INotifyPropertyChanged interface, which is the main mechanism that the rich binding mechanism in Silverlight uses to inform the UI about changes in underlying objects. The abstractions that are implemented in the ListItemBase and the ObservableObjectBase classes make this basic functionality reusable across multiple Silverlight-to-SharePoint projects.

The example in this article focuses on two lists: the Products list and the ProductCategories list. The Products list contains all the product data, and the ProductCategories list contains a set of category names that are referenced by items in the Products list. Having defined the ListItemBase class, a Product class and a ProductCategory class can now be defined to encapsulate the information that is returned from the GetListItems method. Each class must define only those properties that are unique to their respective lists. The ID and Title properties were already defined in the base class.

public class Product : ListItemBase
{
public string ProductNumber
{
get { return _ProductNumber; }
set
{
if (_ProductNumber != value)
{
_ProductNumber = value;
NotifyPropertyChanged("ProductNumber");
}
}
}
private string _ProductNumber;

public ProductCategory ProductCategory
{
get { return _ProductCategory; }
set
{
if (_ProductCategory != value)
{
_ProductCategory = value;
NotifyPropertyChanged("ProductCategory");
}
}
}
private ProductCategory _ProductCategory;

public string ProductModel
{
get { return _ProductModel; }
set
{
if (_ProductModel != value)
{
_ProductModel = value;
NotifyPropertyChanged("ProductModel");
}
}
}
private string _ProductModel;

public string Description
{
get { return _Description; }
set
{
if (_Description != value)
{
_Description = value;
NotifyPropertyChanged("Description");
}
}
}
private string _Description;

public string AttachmentFile
{
get { return _AttachmentFile; }
set
{
if (_AttachmentFile != value)
{
_AttachmentFile = value;
NotifyPropertyChanged("AttachmentFile");
}
}
}
private string _AttachmentFile;
public string OriginalAttachmentFile { get; set; }
}

Product objects contain all the information about a product Unlike the Server or client object models, there is no Context object to track changes to properties of list item objects. Developers who want that functionality must implement it. For example, developers could store a copy of the source XML with each object, or build a shadow copy of each object during the creation of each object.

Now that the Product object is defined, the last step is to convert the XML into objects. In the full version of the .NET framework, the XML could be parsed by using a Dataset object or deserialized by using XML Serialization. Because neither is available in Silverlight, you parse the result XML manually.

To assist with results parsing, encapsulate data access methods, and perform some of the common operations used to access and update list data, consider the following ListAccessBase generic class.

public class ListAccessBase<T> where T : ListItemBase, new()
{
…
public string ListName { get; protected set; }

protected void GetListItemsAsync(string viewName, XElement query, XElement queryOptions, XElement viewFields, string rowLimit)
{
if (string.IsNullOrEmpty(ListName))
throw new ArgumentNullException("ListName");

ListsSoapClient service = CreateListsService();
service.GetListItemsCompleted += new EventHandler<GetListItemsCompletedEventArgs>(ListsService_GetListItemsCompleted);
service.GetListItemsAsync(ListName, viewName, query, viewFields, rowLimit, queryOptions, string.Empty, null);
}

protected void ListsService_GetListItemsCompleted(object sender, GetListItemsCompletedEventArgs e)
{
Exception error = e.Error;
List<T> items = null;

// Check if the SP web service callback reported any exceptions
if (error == null)
{
try
{
ValidateListsCallbackResult(e.Result, true, false, string.Format("Error while getting records from list '{0}'", ListName));

items = new List<T>();

if (e.Result != null && e.Result.HasElements)
{
foreach (XElement row in e.Result.Descendants(XName.Get("row", "#RowsetSchema")))
{
items.Add(FillItemFromXml(row));
}
}

}
catch (Exception ex)
{
error = ex;
}
}

// Raise the completed event supplying the list or the error encountered.
RaiseListLoadingCompletedEvent(items, error);
}

public void SaveItemAsync(T item)
{
if (string.IsNullOrEmpty(ListName))
throw new ArgumentNullException("ListName");

BatchFactory b = new BatchFactory();

XElement batch = b.Batch(b.Update("1", item.Id.ToString(), GetItemFields(item)));

ListsSoapClient service = CreateListsService();
service.UpdateListItemsCompleted += new EventHandler<UpdateListItemsCompletedEventArgs>(service_UpdateListItemsCompleted);
service.UpdateListItemsAsync(ListName, batch);
}

void service_UpdateListItemsCompleted(object sender, UpdateListItemsCompletedEventArgs e)
{
Exception error = e.Error;
List<T> items = null;

// Check if the SP web service callback reported any exceptions
if (error == null)
{
try
{
ValidateListsCallbackResult(e.Result, true, false, string.Format("Error while saving records to list '{0}'", ListName));

items = new List<T>();

if (e.Result != null && e.Result.HasElements)
{
XElement row = (from node in e.Result.Descendants(XName.Get("row", "#RowsetSchema")) select node).FirstOrDefault();
items.Add(FillItemFromXml(row));
}
}
catch (Exception ex)
{
error = ex;
}
}

// Raise the completed event supplying the list or the error encountered.
RaiseListLoadingCompletedEvent(items, error);
}

protected virtual XElement[] GetItemFields(T item)
{
return new XElement[] { GetField("Title", item.Title) };
}

protected virtual T FillItemFromXml(XElement row)
{
T listItem = new T()
{
Id = GetValueFromXml(row, "ID").ToInt32OrDefault(),
Title = GetValueFromXml(row, "Title")
};
return listItem;
}
…
}

The ListAccessBase class contains methods that run the web service calls, parse XML data, parse lookup fields, and validate server responses.

Of particular importance is the FillItemFromXml method, which can be overwritten in a sub-class to parse all the necessary fields for that list. The following code shows the ProductDAL class, which extends the ListAccessBase.

public class ProductsDAL : ListAccessBase<Product>
{

public ProductsDAL(string spUrl)
: base("Products", spUrl) { }

protected override Product FillItemFromXml(XElement row)
{
Product product = base.FillItemFromXml(row);

product.ProductNumber = GetValueFromXml(row, "ProductNumber");
product.ProductModel = GetValueFromXml(row, "ProductModel");
product.Description = GetValueFromXml(row, "Description");
product.AttachmentFile = GetValueFromXml(row, "AttachmentFile");
product.OriginalAttachmentFile = product.AttachmentFile;
product.ProductCategory = new LookupValue(GetValueFromXml(row, "ProductCategory"));

return product;
}

protected override XElement[] GetItemFields(Product item)
{
BatchFactory b = new BatchFactory();
return new XElement[] 
{
b.Field("Title", item.Title),
b.Field("ProductNumber", item.ProductNumber),
b.Field("ProductModel", item.ProductModel),
b.Field("Description", item.Description),
b.Field("AttachmentFile", item.AttachmentFile),
b.Field("ProductCategory", item.ProductCategory.ToLookupValueString())
};
}

public void GetProductsAsync()
{
QueryFactory q = new QueryFactory();
XElement query = null;

GetListItemsAsync(query, GetQueryOptions(true, null, true, true));
}
}

The ListAccessBase class provides generic functionality for communicating with the SharePoint Lists service. The ProductsDAL class provides functionality that is specific to the interaction with the Products list in SharePoint.

There are two methods in this class that override methods in the ListAccessBase class; the GetItemFields and FillItemFromXml methods. The FillItemFromXml method populates the Product object with data from the underlying XML. The GetItemFields method converts the Product object back into XML, which can then be used to update list items.

The last method in the ProductsDAL class is the GetProductsAsync method. It provides the CAML query that is used to get all products in the Products list. You could construct similar methods by using CAML queries to get specific products, such as GetProductsByProductNumberAsync.

Submitting Changes to Data Through the Web Services

With the infrastructure developed so far, it is very easy to update list data. The ProductsDAL class overrides another method from the ListAccessBase class, namely the GetItemFields method. This method converts the object properties into the proper CAML Batch XML format that the Update method requires. You can construct similar CAML Batch XML to create and delete list data.

To update a product, use the following code.

private void SaveItemAsync()
{
ProductsDAL productsDal = new ProductsDAL(AppViewModel.Instance.SharePointSiteURI);
productsDal.ListLoadingCompleted += new EventHandler<Helpers.ListLoadingCompletedEventArgs<Product>>(productsDal_ListSavingCompleted);
productsDal.SaveItemAsync(SelectedProduct);
}

void productsDal_ListSavingCompleted(object sender, Helpers.ListLoadingCompletedEventArgs<Product> e)
{
if (e.Error != null)
throw new Exception(string.Format("Query failed for the {0} List with message {1}", e.ListName, e.Error.Message));

// update SelectedItem fields if necessary
SelectedProduct.OriginalAttachmentFile = e.Result[0].OriginalAttachmentFile;
}

You can use the SaveItemAsync call to save one list item. Or, you can create a SaveItemsAsync call to save multiple items in a single service call because the Update method in the web service uses CAML Batch XML. Although all update requests are batched together, the batch does not run in a single transaction. If an error occurs on a record, other record updates are still committed to SharePoint. To help identify which updates failed, the batch XML uses a method ID that is supplied in the call and which is returned in the results, along with any exceptions. The ID can be any arbitrary string value. Because the ID is supplied in the call and returned in the results, it can be used to tie any exception directly to the request.

To optimize, the updates apply only to the specified fields. The update process does not require you to pass the full record.

Although the XML that is returned by the GetListItems method includes the "ows_" prefix for each field, the prefix should not be included in the update XML. However, spaces do need to be converted to "_x0020_".

The result of an update is XML that looks very similar to the GetListItems XML, and it can be parsed the same way. It contains a fresh copy of the updated items from SharePoint. Although this is not as important in an update operation, the XML in a add operation includes the ID of the newly created item.

SharePoint provides an Error object inside the event arguments object that is included in every call-back method for an async call. For example, if there is a mistake in the CAML that is sent to the server, the Error object contains an exception.

One very important fact to understand about the update process is that SharePoint might include exception information directly in the XML that is returned from the method call, and contain no exception in the Error object. Typically, the absence of an exception in the Error object represents success. However, to determine the success of an update, you must parse each <Result> node in the XML result by using the <ErrorCode> and <ErrorText> nodes to determine whether the specific operation represented by the <Result> node succeeded or failed. Use the ListAccessBase class to parse those errors. The following example code shows the returned XML from an Update call that attempted to update an item that does not exist in the SharePoint list.

<Results xmlns="https://schemas.microsoft.com/sharepoint/soap/">
<Result ID="1,Update">
<ErrorCode>0x81020016</ErrorCode>
<ErrorText>Item does not exist
The page you selected contains an item that does not exist.  It may have been deleted by another user.</ErrorText>
</Result>
</Results>

In this example, the <Result> node has an ID attribute that contains the value that is assigned to the operation when the update request was sent. It is not the ID of a list item. Because more than one operation can be requested in the batch XML that is sent to an update statement, you can use the ID in the <Result> node to match the ID in the batch XML, and determine which operation succeeded or failed.

Adding an Attachment to a SharePoint List Item

One of the main reasons to use SharePoint web services is to perform operations that are not available through the other client APIs; for example, adding an attachment to a SharePoint list item. Although you can read and update data through the other client APIs in SharePoint, you can only add an attachment through web services.

Although Adventure Works products include various assets that have product pictures, the marketing team decided that store a thumbnail image for the product as an attachment to the product item. The code example uses the FileOpenDialog object to enable the user to select a file to upload.

private void AddAttachmentAsync()
{
FileInfo fileInfo = FileToUpload;
FileStream fs = fileInfo.OpenRead();
byte[] fileContents = new byte[fs.Length + 1];
fs.Read(fileContents, 0, (int)fs.Length);

ListsSoapClient service = new ListsSoapClient();
service.AddAttachmentCompleted += new EventHandler<AddAttachmentCompletedEventArgs>(service_AddAttachmentCompleted);
//Attach the Document the Sharepoint Entity
service.AddAttachmentAsync(productsDal.ListName, SelectedProduct.Id.ToString(), fileInfo.Name, fileContents);
}

About the Author

Raul Rodila is a managing partner and senior architect at Arrow Consulting & Design in West Palm Beach, Florida. At Arrow, Raul focuses on large-scale business process automation in the area of audit and finance using SharePoint with Silverlight. Raul is also accomplished as a database architect and performance engineer. Raul is an active member of the .NET and SharePoint community speaking at Code Camps. Raul's company is on the web at www.ArrowDesigns.com.

The following were tech editors on Microsoft SharePoint 2010 articles from Wrox:

  • Matt Ranlett is a SQL Server MVP who has been a fixture of the Atlanta .NET developer community for many years. A founding member of the Atlanta Dot Net Regular Guys, Matt has formed and leads several area user groups. Despite spending dozens of hours after work on local and national community activities, such as the SharePoint 1, 2, 3! series, organizing three Atlanta Code Camps, working on the INETA board of directors as the vice president of technology, and appearing in several podcasts such as .Net Rocks and the ASP.NET Podcast, Matt recently found the time to get married to a wonderful woman named Kim, whom he helps to raise three monstrous dogs. Matt currently works as a senior consultant with Intellinet and is part of the team committed to helping people succeed by delivering innovative solutions that create business value.

  • Jake Dan Attis. When it comes to patterns, practices, and governance with respect to SharePoint development, look no further than Jake Dan Attis. A transplant to the Atlanta area from Moncton, Canada, Dan has a degree in Applied Mathematics, but is 100% hardcore SharePoint developer. You can usually find Dan attending, speaking at, and organizing community events in the Atlanta area, including code camps, SharePoint Saturday, and the Atlanta SharePoint User Group. When he's not working in Visual Studio, Dan enjoys spending time with his daughter Lily, watching hockey and football, and sampling beers of the world.

  • Kevin Dostalek has over 15 years of experience in the IT industry and over 10 years managing large IT projects and IT personnel. He has led projects for companies of all sizes and has participated in various roles including Developer, Architect, Business Analyst, Technical Lead, Development Manager, Project Manager, Program Manager, and Mentor/Coach. In addition to these roles, Kevin also managed a Solution Delivery department as a Vice President for a mid-sized MS Gold Partner from 2005 through 2008 and later also served as a Vice President of Innovation and Education. In early 2010 Kevin formed Kick Studios as a company providing consulting, development, and training services in the specialized areas of SharePoint and Social Computing. Since then he has also appeared as a speaker at numerous user group, summit, and conference type events across the country. You can find out more about Kevin on his blog, The Kickboard.

  • Larry Riemann has over 17 years of experience architecting and creating business applications for some of the world’s largest companies. Larry is an independent consultant who owns Indigo Integrations and does SharePoint consulting exclusively through SharePoint911. He is an author, writes articles for publication and occasionally speaks at conferences. For the last several years he has been focused on SharePoint, creating and extending functionality where SharePoint leaves off. In addition to working with SharePoint, Larry is an accomplished .Net Architect and has extensive expertise in systems integration, enterprise architecture and high availability solutions. You can find him on his blog.

  • Sundararajan Narasiman is a Technical Architect with Content Management & Portals Group of Cognizant Technology Solutions, Chennai, with more than 10 years of Industry Experience. Sundararajan is primarily into the Architecture & Technology Consulting on SharePoint 2010 Server stack and Mainstream .NET 3.5 developments. He has passion for programming and also has interest for Extreme Programming & TDD.

Additional Resources

For more information, see the following resources: