Showing posts with label managed client object model. Show all posts
Showing posts with label managed client object model. Show all posts

Thursday, October 13, 2011

Using Client Object Model, Uploading a new document to a document library which supports different type of Content Types

Sometimes we need to upload new document whose metadata is coming from a csv file. Consider that if the document library supports custom content types then before adding the item we need to know which field columns from the csv file are need to be added to the item. The csv file contains the information of Content Type and all possible Fields as columns in the csv file. Then we need to get the corresponding content type object from the content type string present in the csv file and get all the fields that belong to that content type. Then add the data to those fields only.
Below is the sample code.
We are passing  the parameters
  • Metadat List ,
  • Binary content of the file in byte array,
  • DataRow which contains the metadata information of that document.We are getting this datarow from the DataTable which is built from the csv file,
  •  Item Url :Docuemt file name in our case its a download link from another sharepoint server.
  • Contet Type.
I have written another blog post  related to this blog post that contains some of the methods descriptions like GetMappedListItem
/// <summary>
/// Uploads the document to the document library with metadata
/// </summary>
/// <param name="metaDataList"></param>
/// <param name="content"></param>
/// <param name="dataRow"></param>
/// <param name="itemURL"></param>
/// <param name="contentType"></param>
private void UploadDocument(FieldCollection metaDataList,byte[] content,DataRow dataRow,string itemURL,string contentType)
{
  ClientContext ctx = new ClientContext(targetSiteUrl);
  Web web = ctx.Web;
  FileCreationInformation newFile = new FileCreationInformation();
  newFile.Content = content;
  newFile.Url = itemURL.Substring(itemURL.LastIndexOf("/") + 1);
  List docs = web.Lists.GetByTitle(targetListName);
  ctx.Load(docs);
  ctx.ExecuteQuery();
  Microsoft.SharePoint.Client.File uploadFile = docs.RootFolder.Files.Add(newFile);
  ctx.Load(uploadFile);
  ctx.ExecuteQuery();
  ListItem item = uploadFile.ListItemAllFields;
  //Set the metadata     
  item["Title"] = newFile.Url.Substring(0,newFile.Url.IndexOf("."));

  ContentType targetDocumentSetContentType = GetContentType(ctx,docs,contentType);
  item["ContentTypeId"] = targetDocumentSetContentType.Id.ToString();
  item.Update();
  ctx.ExecuteQuery();
           
  foreach (Field field in metaDataList)
  {
   if (!field.Hidden && !field.ReadOnlyField)
      {
        if (field.InternalName != "FileLeafRef" && field.InternalName != "ContentType" && !field.ReadOnlyField && field.Title != "Title")
          {
           if(field.FieldTypeKind==Microsoft.SharePoint.Client.FieldType.Lookup)
             {
               FieldLookup lookupField =field as FieldLookup;
               ListItem mappedListItem = null;

              if (lookupField != null)
              {
                mappedListItem = GetMappedListItem(lookupField, dataRow[field.Title].ToString());
              }
              if (mappedListItem != null)
              {
               FieldLookupValue lookupValue = new FieldLookupValue();
               lookupValue.LookupId = mappedListItem.Id;
               item[field.InternalName] = lookupValue;
              }
           }
           else
             item[field.InternalName] = dataRow[field.Title];
        }
    }
  }
           
 item.Update();
 ctx.ExecuteQuery();
 ctx.Dispose();
 }

Below is the code to Get the content types

/// <summary>
/// Gets the Content Type Object of the given content type string
/// </summary>
/// <param name="ctx"></param>
/// <param name="docs"></param>
/// <param name="contentType"></param>
/// <returns></returns>
private ContentType GetContentType(ClientContext ctx,List docs,string contentType)
{
  ContentTypeCollection listContentTypes = docs.ContentTypes;
  ctx.Load(listContentTypes, types => types.Include
           (type => type.Id, type => type.Name,
             type => type.Parent));
  var result = ctx.LoadQuery(listContentTypes.Where(c => c.Name == contentType));
  ctx.ExecuteQuery();
  ContentType targetDocumentSetContentType = result.FirstOrDefault();
  return targetDocumentSetContentType;
}

Adding a new document Item whose metadata contains lookup fields using Client Object Model

It is easy to add new document item using client object model. In our case we are having all metadata corresponding to the item in a csv file and the csv file contains the look up filed value.
So we have the metadata including the lookup value, but whenever you try to execute the below code you will end up with an exception "Invalid data has been used to update the list item. The field you are trying to update may be read only".

I have written another blog post  related to this blog post that contains some more information
ClientContext ctx = new ClientContext(targetSiteUrl);
Web web = ctx.Web;
FileCreationInformation newFile = new FileCreationInformation();
newFile.Content = content;
newFile.Url = itemURL.Substring(itemURL.LastIndexOf("/") + 1);
List docs = web.Lists.GetByTitle(targetListName);
ctx.Load(docs);
ctx.ExecuteQuery();
Microsoft.SharePoint.Client.File uploadFile = docs.RootFolder.Files.Add(newFile);
ctx.Load(uploadFile);
ctx.ExecuteQuery();
ListItem item = uploadFile.ListItemAllFields;
//Set the metadata     
item["Title"] = newFile.Url.Substring(0,newFile.Url.IndexOf("."));
item["Lookup Field"]=someLookupValue;  //this is the cause of the error.     

ContentType targetDocumentSetContentType = GetContentType(ctx,docs,contentType);
item["ContentTypeId"] = targetDocumentSetContentType.Id.ToString();
item.Update();
ctx.ExecuteQuery();
           

Then you don’t know what to do and which is causing the error. As shown in the below code, the highlighted line is causing the error. We can only set  FieldLookupValue
Object to the Fields of type Lookup Field. Instead of setting the FieldLookupValue if we set the text filed to it, it will throw the above error. So the solution is to set the  FieldLookupValue object to the lookup field.

The code follows below.
if (lookupField != null)
{
      mappedListItem = GetMappedListItem(lookupField, dataRow[field.Title].ToString());
}
if (mappedListItem != null)
{
     FieldLookupValue lookupValue = new FieldLookupValue();
     lookupValue.LookupId = mappedListItem.Id;
     item[field.InternalName] = lookupValue;
}


If you observe the above code, we are setting the LookupId only, we cannot set the LookupValue field as it is read only filed. But I have Lookup Value with me in the csv file. Then how can I get the ID value of the corresponding lookup value from the base list. The below code solves our problem.
Steps included in the code are  
  1.  Get the Web Object based on the LookupWebId present in the FieldLookup object.
  2.  Get the Look up list from the web based on the List ID present in the FieldLookup object.
  3.  Get the List item with its id based on the Lookup Value we have. 
 /// <summary>
        /// Gets corresponding Lookup field's base list item
        /// </summary>
        /// <param name="lookupField"></param>
        /// <param name="lookupValue"></param>
        /// <returns></returns>
        private ListItem GetMappedListItem(FieldLookup lookupField,string lookupValue)
        {
            string mappingField = lookupField.LookupField;

            ClientContext mappedWebContext = new ClientContext(tbRootWebUrl.Text);

            Web web = mappedWebContext.Web;
            Web mappedWeb = GetLookupWeb(lookupField, web);
            //if(mappedWeb.Id==lookupField.LookupWebId)
            if (mappedWeb == null)
                return null;
            mappedWebContext.Load(mappedWeb, p => p.Id, p => p.Title);
            List lookupList = mappedWeb.Lists.GetById(new Guid(lookupField.LookupList));
            mappedWebContext.Load(lookupList);
            mappedWebContext.ExecuteQuery();

            ListItemCollection libListItems = lookupList.GetItems(CamlQuery.CreateAllItemsQuery());

            mappedWebContext.Load(libListItems, items => items.Include(
                    itemlookup => itemlookup.Id,
                    itemlookup => itemlookup[mappingField]));
            mappedWebContext.ExecuteQuery();
            foreach (ListItem mappedItem in libListItems)
            {
                if (mappedItem[mappingField].ToString() == lookupValue)
                {
                    mappedWebContext.Dispose();
                    return mappedItem;
                }
            }
            mappedWebContext.Dispose();
            return null;
        }

        private Web GetLookupWeb(FieldLookup lookupField,Web web)
        {
            ClientContext mappedWebContext = web.Context as ClientContext;

            Web mappedWeb = null;
            mappedWebContext.Load(web, webitm => webitm.Id, webitm => webitm.Title, webitm => webitm.ServerRelativeUrl);
            mappedWebContext.ExecuteQuery();
            if (web.Id == lookupField.LookupWebId)
            {
                return web;
            }
            WebCollection webCollection = web.Webs;
            mappedWebContext.Load(webCollection, webitems => webitems.Include(itemweb => itemweb.Title, itemweb => itemweb.Id,itemweb=>itemweb.ServerRelativeUrl));
            mappedWebContext.ExecuteQuery();
            foreach (Web webitem in webCollection)
            {
                if (webitem.Id == lookupField.LookupWebId)
                    return webitem;
                mappedWeb=GetLookupWeb(lookupField,webitem);
                if (mappedWeb != null)
                    break;
            }
            return mappedWeb;
        }

Wednesday, August 24, 2011

SharePoint 2010 Managed Client Object Model. How to Get Site Properties?


Silverlight applications use this Managed Client Object Model .In the Client Object Model, each line of statement that requires some site information is a request to the server, the server responds to the request and the Response is packed into a Client Object which is easy to use. In Client Object Model we cannot get all the required data on a single request as the data is too big SharePoint uses On Demand serve request methodology for the Client Object Model. The developer should take care when getting data from the server.He should request minimal information he needs from the Server using LINQ filter queries.

Below is the code to Get Site Properties.

PropertyValues pValues = null;
string siteCollectionUrl = string.Empty;  // stores sitecollection url;
string siteUrl = http://yoursitename ; // site url, may be site under a sitecollection;
string PropertyIDValue = string.Empty;



private void LoadSite()
        {
            ClientContext ctx = new ClientContext(siteUrl);
            siteCollection = ctx.Site;                     
            ctx.Load(siteCollection, s => s.Url);
            ctx.ExecuteQueryAsync(GetSiteRequestSucceeded, RequestFailed);
        }

        private void GetSiteRequestSucceeded(object sender, SP.ClientRequestSucceededEventArgs e)
        {
            Dispatcher.BeginInvoke(() =>
            {
                siteCollectionUrl = siteCollection.Url;
                ClientContext ctx = new ClientContext(siteUrl)
                currentWeb = ctx.Web;
                ctx.Load(currentWeb, w => w.Title, w => w.ServerRelativeUrl, w => w.AllProperties);
                ctx.ExecuteQueryAsync(GetWebRequestSucceeded, RequestFailed);
            });
        }

        private void GetWebRequestSucceeded(object sender, SP.ClientRequestSucceededEventArgs e)
        {
            Dispatcher.BeginInvoke(() =>
            {
                pValues = currentWeb.AllProperties;
         ClientContext ctx = new ClientContext(siteUrl) ;
         ctx.Load(pValues);
                context.ExecuteQueryAsync(GetPropsSuccess, RequestFailed);
                }
            });
        }

        private void GetPropsSuccess(object sender, SP.ClientRequestSucceededEventArgs e)
        {
            Dispatcher.BeginInvoke(() =>
            {
              this.PropertyIDValue = (string)pValues.FieldValues["propertyid"];
             
            });
        }

       

private void RequestFailed(object sender, SP.ClientRequestFailedEventArgs e)
        {
            Dispatcher.BeginInvoke(() =>
            {
                MessageBox.Show("Error occured:  " + e.Exception);
            });
        }