Monday, May 17, 2010

SharePoint 2010: Add/Delete/update/search list items with Managed Client Object Model

Managed client Object model is a rich extension to SharePoint 2010. You can almost perform all kinds operations against SharePoint using Client OM. In this post I’ll show you how to use Managed client Object Model (OM) to manipulate list items. The code snippet I’ll use in the examples below is depend on a product list. The product list has four fields: Product Name, Product Description, Product launch date, available quantity. The product list is represented with a class as shown below:

public class Product
{
    public string ProductName { get; set; }
    public int ProductID { get; set; }
    public string ProductDescription { get; set; }
    public DateTime LaunchDate { get; set; }
    public int AvailableQuantity { get; set; }
}

Add new list item

To add a new item in a list you may or may not need to pass ListItemCreationInformation.  Let me explain when you need to pass ListItemCreationInformation or when not:

  • If you need to add item at the root (not under a subfolder) then you can ignore ListItemCreationInformation by passing null in place of ListItemCreationInformation.
  • If you need to add item somewhere inside subfolder then you need to create an instance of ListItemCreationInformation and set it’s Folderurl to url of the folder you want to save the list item.

The code snippet below add a new list item (Product item in my case):

public void AddNewProduct(string siteUrl, string folderPath, Product product)
{
    const string listName = "Product";
    using (var clientContext = new ClientContext(siteUrl))
    {
        var list = clientContext.Web.Lists.GetByTitle("Product");

        ListItemCreationInformation listItemCreationInformation = null;
        if (!string.IsNullOrEmpty(folderPath))
        {
            listItemCreationInformation = new ListItemCreationInformation();
            listItemCreationInformation.FolderUrl = string.Format("{0}/lists/{1}/{2}", siteUrl, listName, folderPath);
        }

        var listItem = list.AddItem(listItemCreationInformation);
        listItem["ProductName"] = product.ProductName;
        listItem["ProductDescription"] = product.ProductDescription;
        listItem["LaunchDate"] = product.LaunchDate;
        listItem["AvailableQuantity"] = product.AvailableQuantity;
        listItem.Update();
        clientContext.ExecuteQuery();

    }
}

The method in above code snippet takes a site url  and optional folder url and a product. If you want to add item in the root folder then you can pass folderPath parameter empty or null in the above method. Depending on whether you have passed folderPath or not, the ListItemCreationInformation will be initialized or not. If you don’t pass folderPath, the ListItemCreationInformation variable will be null. but if you want to add item in a specific subfolder then pass the folder name as parameter and the ListItemCreationInformation will be initialized and teh FolderUrl of the instance will be set with parameter folderaPath.

Delete list item

In the code snippet below, I have used the same product list. I have passed the product Id as parameter and the get the list item by id. Once I get the list item, I have invoked the DeleteObject method of the list item.

public void DeleteProduct(string siteUrl, int productId)
{
    using (var clientContext = new ClientContext(siteUrl))
    {
        var list = clientContext.Web.Lists.GetByTitle("Product");
        var product = list.GetItemById(productId);
        product.DeleteObject();
        clientContext.ExecuteQuery();
    }
}

Update List Item

The following code snippet shows how to update a product list.

public void UpdateProduct(string siteUrl, Product product)
{
    using (var clientContext = new ClientContext(siteUrl))
    {
        var list = clientContext.Web.Lists.GetByTitle("Product");
        var productToModify = list.GetItemById(product.ProductID);
        productToModify["ProductName"] = product.ProductName;
        productToModify["ProductDescription"] = product.ProductDescription;
        productToModify["LaunchDate"] = product.LaunchDate;
        productToModify["AvailableQuantity"] = product.AvailableQuantity;
        productToModify.Update();
        clientContext.ExecuteQuery();
    }
}

Get a single List Item

To get a lsit item you need to specify the fields you want to retrieve from the list item.

public Product GetProductById(string siteUrl, int productId)
{
    Product product = null;
    try
    {

        using (var clientContext = new ClientContext(siteUrl))
        {
            var list = clientContext.Web.Lists.GetByTitle("Product");
            var productItem = list.GetItemById(productId);
            clientContext.Load(productItem, pi => pi.Id, pi => pi["ProductName"], pi => pi["ProductDescription"], pi => pi["AvailableQuantity"], pi => pi["LaunchDate"]);
            clientContext.ExecuteQuery();
            product = new Product();
            product.AvailableQuantity = Convert.ToInt32(productItem["AvailableQuantity"]);
            product.LaunchDate = Convert.ToDateTime(productItem["LaunchDate"]);
            product.ProductDescription = productItem["ProductDescription"].ToString();
            product.ProductID = productItem.Id;
            product.ProductName = productItem["ProductName"].ToString();
        }
    }
    catch (Exception exception)
    {
        Console.WriteLine(exception.Message);

    }
    return product;
}

As shown in the example below, the field values I like to retrieve are passed in the ClientContext.Load method. If you want to retrive all fields (which is not recommended) then call the Load method with the list item, as shown below:

clientContext.Load(productItem);

Get Collection of List Items

To get a list of items as per any condition, you can use CAML  query (or in Linq to SharePoint extension). I’ve covered in another post on how to use Linq to SharePoint. One important point to notice here in the code snippet below is that if you want to look for items inside subfolders thenyou need to add an extra attribute Scope=’RecursiveAll’ in view tag.

public IList<Product> FindProductsByName(string siteUrl, string productName, bool includeSubfolder)
{
    IList<Product> products = new List<Product>();
    try
    {
        string scope = string.Empty;
        if (includeSubfolder)
        {
            scope = "Scope='RecursiveAll'";
        }
        

        using (var clientContext = new ClientContext(siteUrl))
        {
            List list = clientContext.Web.Lists.GetByTitle("Product");
            CamlQuery query = new CamlQuery();
            query.ViewXml = string.Format(@"<View {0}>
                                <Query>
                                    <Where>
                                    <Contains>
                                        <FieldRef Name='ProductName'/>
                                        <Value Type='Text'>{1}</Value>
                                    </Contains>
                                    </Where>
                                </Query>
                                </View>", scope, productName);
            ListItemCollection listItems = list.GetItems(query);
            clientContext.Load(listItems, li => li.Include(pi => pi.Id, pi => pi["ProductName"], pi => pi["ProductDescription"], pi => pi["AvailableQuantity"], pi => pi["LaunchDate"]));
            clientContext.ExecuteQuery();

            foreach (var productItem in listItems)
            {
                var product = new Product();
                product.AvailableQuantity = Convert.ToInt32(productItem["AvailableQuantity"]);
                product.LaunchDate = Convert.ToDateTime(productItem["LaunchDate"]);
                product.ProductDescription = productItem["ProductDescription"].ToString();
                product.ProductID = productItem.Id;
                product.ProductName = productItem["ProductName"].ToString();
                products.Add(product);
            }
        }
    }
    catch (Exception exception)
    {
        Console.WriteLine(exception.Message);

    }
    return products;
}

You can notice that I have passed the fields I want to load as per the query result in ClientContext.Load’s li.Include extension method. If you want to load all properties then you can call the Load method in a way as shown below:

clientContext.Load(listItems, li => li);

 

If you don’t specify a field to load but try to access it then you will get an exception of type ‘PropertyOrFieldNotInitializedException’ with the following message:

The property or field has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested.

25 comments:

  1. Hello,

    Great article!

    I'm trying to getting a list containing a choice field. I'm getting the error "The property or field has not been initialized".

    if I do the same code with string or int fields, I have no problems...

    Any idea?

    Thank you,

    Eric

    ReplyDelete
  2. The error comes up when you try to access a property that has not loaded on client side. Please make sure the choice field is loaded. So the choice filed you are trying to access has not been loaded. As I can remember I could load choice filed without any problem.

    ReplyDelete
  3. Thank you for your quick reply...

    That what I tried...

    clientContext.Load(listItems, li => li.Include(pi => pi.Id, pi => pi["Company"]));

    Where Company is the choice field, but it is not working, or I'm doing something wrong?

    ReplyDelete
  4. Hi Eric,

    I had tried the same code you are facing problem with on my server on choice field and it's worked. The code looks ok. Can you plz check if there's any permission related issue or are you missing something else. I'll suggest you to load a single item's all properties as shown in my function GetProductById. In the method GetProductById use clientContext.Load(productItem) only instead of passing properties names. This will load all properties of the productitem. Then try to access the choice filed.

    ReplyDelete
  5. Hi,

    and what about updatint the lookup field? very usefull information

    ReplyDelete
  6. For lookup filed you need to use 'Microsoft.SharePoint.Client.FieldLookupValue'. For setting the value of lookup filed create an object instance of FieldLookupValue and set the field's value. For getting a lookup field value cast the listitem[fieldname] value to FieldLookupValue and then use.

    ReplyDelete
  7. Hi

    Great article!
    Does this work for all kinds of lists or only on document libraries? I tried this code but it doesn't seem to add an item to a folder, but it isn't giving an error either.

    Thanks

    Kris

    ReplyDelete
  8. it only works for list. For manipulating doc libraries, you can follow my another post: http://ranaictiu-technicalblog.blogspot.com/2010/06/sharepoint-2010-attach-files-to.html

    ReplyDelete
  9. OK, I forgot to set clientContext.ExecuteQuery();
    This clientContext is new to sharepoint 2010, right? I spend days looking for a solution to do this on Sharepoint 2007. I eventually ended up doing this with the Lists Webservice. But this solution is so much nicer. Thanks again.

    ReplyDelete
  10. whats the efficient way of getting total number of list items based on a condition. For example , Leave type = Sick and username = xyz

    ReplyDelete
  11. @Hamza, I'm not sure if there any easiest way to getting the count. CAML doesn't support aggregate functions (sum, count etc). One simple but not efficient solution might be to get the items with CAML and then get the result items count.

    ReplyDelete
  12. any ideas on how to update the 'managed enterprise keywords' column for an individual item?

    ReplyDelete
  13. how to get the sub folder or item ?

    ReplyDelete
  14. @nsp, please look at my another post: http://ranaictiu-technicalblog.blogspot.com/2010/03/sharepoint-2010-manage.html

    ReplyDelete
  15. How to get Collection of List Items by using LINQ but in SharePoint Client Object Model not in LINQ TO SHAREPOINT ?

    ReplyDelete
  16. @Driss, You can apply Linq to ListItemCollection (the result returned by ClientContext) but this will be applied on client side. So while you are applying the linq on listItemCollection, the data is on client pc. So if there's large amount of data coming from server, you should apply filter to the request rather than getting all data in response then applying linq on client.

    ReplyDelete
  17. I am trying to retrieve list items using below CAML query passing Id, Title and Coments as parameters.

    Its working fine in all cases except this - I have an item in the list which has Id but Title and Coments columns are empty.
    The below query is not retrieving that row even though I pass empty string parameters to these 2 fields.

    qryDv.Query = "" + id + ""
    + "" + title + "" + "" + coments + "";


    I tried using but guess my syntax was wrong. Any ideas how to retrieve that item with empty values Sohel? Thank you.

    ReplyDelete
  18. sorry..this is the query for above post..i have removed lessthan and greaterthan symbols as they are getting truncated.

    Where And And Eq FieldRef Name='ID' Value Type='Number'" + id + "value /Eq"+ "Eq FieldRef Name='Title' / Value Type='Text' " + title + "/value /Eq /And"+ "Eq FieldRef Name='Coments' / Value Type='Text' " + coments + "/value></Eq /And /Where";

    ReplyDelete
  19. Hi

    What is the advantage of using "Managed client Object Model (OM)"? event we can do all these operations with native sharepoint library that we used to do in SharePoint 2007.

    ReplyDelete
  20. I am trying to create a list item using the code above. When I call ExecuteQuery() I receive an error: "The given key was not present in the dictionary." I'm not setting anything special just Title.

    ReplyDelete
  21. Most probably you are misspelled a field name or list name. The error might be where you are using indexer ([])

    ReplyDelete
  22. Brilliant blog post Sahel. I want to follow your post to get items from a list to populate a pop up. I have a sharePoint List with 2 fields, country and description. I also have an image map in html with hotspots corresponding to the countries. I would like to create a script that I can paste into a Content Editor web Part so that when someone hovers over a hotspot on the image map, a pop up displays with the information (title and description) form the list. I am new to javascript so need your help. Can you assist me? Your script retrieves items from a list but how to use it to populate items in a pop up on screen when a user hovers over an item.

    ReplyDelete
  23. @Tenille, I think you can google map javascript api to put marker in the country and maybe show more information of mouse over. Please check google map javascript api: https://developers.google.com/maps/documentation/javascript/

    ReplyDelete