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
}
}
}