Linq to SharePoint. Cross site collection queries
In this post I show how to execute Cross-SiteCollection queries in SharePoint using Linq to SharePoint.
Linq to SharePoint natively can only work in one Site Collection (SPSite). This limitation related to SPServerDataConnection object that provdes data connection to lists (or document libraries) in Linq to SharePoint. More specifically, the problem is with the SPServerDataConnection's constructor:
- public SPServerDataConnection(string url)
- {
- if (SPContext.Current != null)
- {
- this.defaultSite = SPContext.Current.Site;
- this.defaultWeb = (SPContext.Current.Web.Url == url)
- ? SPContext.Current.Web
- : this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
- }
- else
- {
- this.defaultSite = new SPSite(url);
- this.defaultWeb = this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
- }
- if (!this.defaultWeb.Exists)
- {
- throw new ArgumentException(Resources.GetString("CannotFindWeb",
- new object[] { url }));
- }
- this.defaultWebUrl = this.defaultWeb.ServerRelativeUrl;
- this.openedWebs = new Dictionary<string, SPWeb>();
- this.openedWebs.Add(this.defaultWebUrl, this.defaultWeb);
- }
In other words, if SPContext is available we can't initialize data provider for other site collection. To overlimit this restriction we have set current context (SPContext.Current) to null and then SharePoint will initialize new SPSite object from URL.
Cross-SiteCollection Repository
To encapsulate Cross-SiteCollection queries into repository follow: add a new constructor that takes a boolean argumeny representing initialization for another site collection.
- protected BaseRepository(string listName, string webUrl, bool readOnly, bool crossSite)
- {
- ReadOnly = readOnly;
- ListName = listName;
- WebUrl = webUrl;
-
- var ctx = SPContext.Current;
- IsAnonymous = ctx != null && SPContext.Current.Web.CurrentUser == null;
- if (crossSite)
- {
- var backupCtx = HttpContext.Current;
- // Reset the context
- HttpContext.Current = null;
- // Initialize a new context and others
- InitializeParameters();
- HttpContext.Current = backupCtx;
- }
- else
- {
- InitializeParameters();
- }
- }
Using
There is CrossSiteCollectionDataQuery webpart in my demo project that retrieves data from two lists located on two different site collections. CreateChildControls method in it is this:
- // Another site collection
- var repository1 = new EmployeeRepository("http://SharePointServer/linq", true, true);
- var items1 = repository1.GetEntityCollection();
- foreach (var employee in items1)
- {
- // Render information about an employee
- RenderEmployeeInfo(employee);
- }
- Controls.Add(new LiteralControl("***<br/>"));
- // Current site collection
- var repository2 = new EmployeeRepository("http://SharePointServer/sites/asc", true, false);
- var items2 = repository2.GetEntityCollection();
- foreach (var employee in items2)
- {
- // Render information about an employee
- RenderEmployeeInfo(employee);
- }
Result of code above looks like this: