1

Just wondering which one would be the best method for achieving below mentioned requirement or is there any other way of doing it. This is just a one time activity hence performance doesn't matter.

Requirement:- I have to get all the contents from SharePoint Online site where particular sitecolumn is used. My sitecolumn is of type ManagedMetadata and is added to n number of content types.

Below are the solutions which I thought to achieve the same.

Soln 1) I can use Search API to get the contents, but if some lists or libraries are marked as not show the contents in search then I wont get it.

Soln 2) I can loop through all the subsites, lists and libraries to get the data.

SPSiteDataQuery could have made it easier but in CSOM its not available.

other solutions or suggestions are welcome !!

2 Answers 2

3

Regarding option 2,you could consider the following CSOM solution which mimics to some extent SSOM SPSiteDataQuery class:

using System.Linq.Expressions;
using Microsoft.SharePoint.Client;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SharePoint.Client.Extensions
{
    public static class WebExtensions
    {
        /// <summary>
        /// Retrieve list data from site collection 
        /// </summary>
        /// <param name="parentWeb"></param>
        /// <param name="serverTemplateId">List Template Id</param>
        /// <param name="query">CAML Query</param>
        /// <returns></returns>
        public static List<ListDataResult> GetSiteData(this Web parentWeb, int serverTemplateId, CamlQuery query)
        {
            var results = new List<ListDataResult>();
            GetSiteData(parentWeb, serverTemplateId, query, ref results);
            return results;
        }



        private static void GetSiteData(this Web parentWeb, int serverTemplateId, CamlQuery query, ref List<ListDataResult> results)
        {
            Expression<Func<Web, object>>[] expr = { w => w.Lists.Where(l => l.BaseTemplate == serverTemplateId), w => w.Id}; 
            var ctx = parentWeb.Context;
            var qry = new SubwebQuery();
            var subWebs = parentWeb.GetSubwebsForCurrentUser(qry);
            ctx.Load(parentWeb, expr);
            ctx.Load(subWebs,wcol => wcol.Include(expr));
            ctx.ExecuteQuery();
            if (results.Count == 0)
                RetrieveAndProcessListsResults(results,parentWeb,query);
            if (subWebs.Count > 0)
            {
                foreach (var web in subWebs)
                {
                    RetrieveAndProcessListsResults(results, web, query);
                    GetSiteData(web,serverTemplateId,query,ref results);
                }
            }
        }


        private static void RetrieveAndProcessListsResults(List<ListDataResult> results, Web web, CamlQuery query)
        {
            var allItems = new Dictionary<List,ListItemCollection>();
            var ctx = web.Context;
            foreach (var list in web.Lists)
            {
                var items = list.GetItems(query);
                ctx.Load(list, l => l.Fields, l => l.Id);
                ctx.Load(items);
                allItems[list] = items;
            }
            ctx.ExecuteQuery();
            foreach (var items in allItems)
            {
                var result = new ListDataResult();
                result.WebId = web.Id;
                result.ListId = items.Key.Id;
                result.Data = items.Value.Select(v => v.FieldValues).ToList();
                results.Add(result);
            }
        }

    }

    public class ListDataResult
    {
        public Guid WebId { get; set; }

        public Guid ListId { get; set; }

        public List<Dictionary<string,object>> Data { get; set; }
    }
}

Gist

Usage

The following example shows how to retrieve all items from Pages library across site collection:

using (var ctx = GetContext(webUri, userName, password))
{
    var rootWeb = ctx.Site.RootWeb;
    var results = rootWeb.GetSiteData(850,CamlQuery.CreateAllItemsQuery());  
    //print
    foreach (var res in results)
    {
        Console.WriteLine("Web: {0} List: {1}",res.WebId,res.ListId);
        foreach (var item in res.Data)
        {
            Console.WriteLine("Title: {0}", item["Title"]); //print title
        }
    }
}
1
  • 1
    Thanks a lot Vadim for your quick response and also for your ultimate solution. I really appreciate the way you provided a clean code. Thanks once again !
    – Azam Khan
    Commented Nov 19, 2015 at 12:56
1

I would lean towards solution 1, utilizing search, understanding the limitation you specified.

This has several benefits, mainly the server is doing the work for you crawling and identifying this content. It is fast. It is security trimmed.

This highly depends on what you are doing though. If it is for something end user facing, it is the right way to go. If it is an administrative/compliance/legal thing, then solution 2 would be better. If it is to serve both functions, then do both. Search for the end users and managed code (CSOM or Powershell) to give an exhaustive list.

1
  • Thanks Eric for your quick response and advise, I also preferred the second solution and achieved it using CSOM.
    – Azam Khan
    Commented Nov 19, 2015 at 12:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.