Monday, April 19, 2010

Sharepoint lists Web service

Recently I had a requirement to get data from a project management product and push it into a sharepoint list on a set interval.
The steps involved were:

1. Delete all items from current sharepoint list

  1. Get data from project management software
  2. Do some mapping and push into sharepoint

SSIS was going to be the vehicle to run the code I just had to figure out how to talk to sharepoint.

This was the first time I had attempted to do this so I relied mostly on Google to show me the way but it wasnt so straight forward. There are a few niggles to be aware of.

My approach was:

  1. Get All current sharepoint list items (for the IDs) - GetListItems
  2. Delete each one from sharepoint (Using the IDs) - UpdateListItems
  3. Get new data rows
  4. Push each one to sharepoint - UpdateListItems

Before we get started there's a few things we need to know:

  • Url to sharepoint lists web service

    eg. http://someLocationSomewhere/SCP2010/_vti_bin/lists.asmx - add this as a web reference to your project.
  • List GUID - From here Go to the list settings page. Right click on "Title, description and navigation" and copy the URL. Paste that into notepad and copy everything after "List="in the string. That's your URL Encoded GUID for the list
  • View GUID- You can google this yourself. You can get it either by url hack or web service calls

Ok, first thing, how to get list items?

Lists list = new Lists()

{

Url = ServiceUrl,

Credentials = System.Net.CredentialCache.DefaultCredentials

};

XmlNode resultsNode = list.GetListItems(ListGUID, ViewGUID, null, null, null, null, null);

ServiceUrl, ListGUID and ViewGUID are class properties.
The above code will give you an XmlNode containing your list items. View the InnerXml in VS. You can iterate them using standard Xml node iteration techniques.

foreach (XmlNode outerNode in resultsNode.ChildNodes)

{

if (outerNode.NodeType.Equals(System.Xml.XmlNodeType.Element))

{

foreach (XmlNode node in outerNode.ChildNodes)

{

if (node.NodeType.Equals(System.Xml.XmlNodeType.Element))

{

XmlNode idNode = node.Attributes.GetNamedItem(IDFieldName);

try

{

DeleteListItem(idNode.InnerText);

Dts.Log("Deleted ID: " + idNode.InnerText, 997, null);

}

catch (Exception ex)

{

Dts.Log("An error occurred during DeleteListItem: " + ex.Message, 998, null);

if (!ContinueOnError)

throw ex;

}

}

}

}

}


The important bit in this code is IDFieldName. This attribute has the ID of the item we want to delete.

public string IDFieldName

{

get

{

return "ows_ID";

}

}

How to Delete a list item?

Now we have the ID of the list item, we can delete it.

I created a method that takes the ID and does the delete using UpdateListItems()

private void DeleteListItem(string itemId)

{

XmlDocument xmlDoc = new System.Xml.XmlDocument();

XmlElement elBatch = xmlDoc.CreateElement("Batch");

elBatch.SetAttribute("ViewName", ViewGUID);

elBatch.InnerXml = string.Format("{0}", itemId);

Lists list = new Lists()

{

Credentials = System.Net.CredentialCache.DefaultCredentials,

Url = ServiceUrl

};

XmlNode ndReturn = list.UpdateListItems(ListGUID, elBatch);

if (!ndReturn.InnerText.Equals(SuccessResult))

throw new ApplicationException(ndReturn.InnerText);

}


I used the returned XmlNode.InnerText to test for Success. The sharepoint web service returns

"0x00000000"
when it succeeds.

public string SuccessResult

{

get

{

return "0x00000000";

}

}



Now the final step, How to Add List Items?

I created a method called AddListItem(). Pretty much the same code as DeleteListItems() except we build the xml stub with our new data and pass it to AddListItem() to be submitted to sharepoint via UpdateListItems().

private void AddListItem(string spAddItemXML)

{

XmlDocument xmlDoc = new System.Xml.XmlDocument();

XmlElement elBatch = xmlDoc.CreateElement("Batch");

elBatch.SetAttribute("OnError", "Continue");

elBatch.SetAttribute("ViewName", ViewGUID);

elBatch.InnerXml = spAddItemXML;

Lists list = new Lists()

{

Url = ServiceUrl,

Credentials = System.Net.CredentialCache.DefaultCredentials

};

XmlNode ndReturn = list.UpdateListItems(ListGUID, elBatch);

if (!ndReturn.InnerText.Equals(SuccessResult))

throw new ApplicationException(ndReturn.InnerText);

}


The xml stub you pass in to this method will depend on the structure of your list but the basic xml structure is:

Container: <Method ID="MyId" Cmd="New">
Row: <Field Name="MyFieldName">myFieldValueField>

Thats pretty much it, remember to always specify the Url even though the web service reference already knows it.

Wednesday, March 31, 2010

Bids4biz launch coming soon

We're getting closer to launch for bids4biz. I dont want to say a specific date right now but we do have a date set.

Dev has gone well, Ive been able to maximise code reuse without getting lost in abstraction. Theres a few other things I would like to implement like a "one click button user control" that uses javascript to disable the button after the first click and displays a throbber gif and perhaps even changes the button text to "Loading..." or "Please wait". Ill have to expose ValidationGroup, Text and the click event.

I still have a few issues with some of my user controls.
I have a UC which has a ModalPopup with login controls with validation. If I have multiple of these UC on the same page they fire each others validation which is not intended behaviour. Ive tried setting ValidationGroup to be unique (random generated string) on UC Init but still fires other instances validation. Will have to figure this one out soon.


Tuesday, January 26, 2010

Plinqo for bids4biz

We've been developing an exciting new website called Bids4biz.com.
I wont go into detail about the actual site yet (until we are closer to launch) but I will say a bit about my experience with PLINQO .

What is PLINQO?

Basically it's what LINQ to SQL should've been.
PLINQO is an ORM built on top of LINQ to SQL implemented as Codesmith templates so you'll need a copy of Codesmith to use it.

Why I chose PLINQO

Before I begun the project I had a few requirements in mind when deciding what framework to use for my business and data access layers:
  • Code should be easily generated from Database schema
  • Changes to my database schema should easily propagate to my code through code re-generation
  • The code Generation tool should be simple to use
  • Documentation should be readily available
  • There should exist some implementation in the business layer objects to enforce business rules
  • Performance should be good (for DB transactions)
  • Objects should all have strongly typed properties.
  • Should not be overly complex
  • Learning curve not too steep
I looked at CSLA, nHibernate, Doodads, building from the ground up and of course PLINQO.
PLINQO by far fit my criteria above.

All I had to worry about was designing the DB then after specifying a few settings (with help from the video tutorials which were very easy to follow) then Codesmith generated all my classes and dbml from within Visual Studio.
If I make a change to my database, after 2-3 clicks, my code is updated to match.

I only needed to spend 30mins learning how it worked before I could jump right in and generate some code.


Development is still going and so is my learning experience with PLINQO but so far it has been an excellent choice.