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:

  1. public SPServerDataConnection(string url)
  2. {
  3.     if (SPContext.Current != null)
  4.     {
  5.         this.defaultSite = SPContext.Current.Site;
  6.         this.defaultWeb = (SPContext.Current.Web.Url == url) 
  7.             ? SPContext.Current.Web 
  8.             : this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
  9.     }
  10.     else
  11.     {
  12.         this.defaultSite = new SPSite(url);
  13.         this.defaultWeb = this.defaultSite.OpenWeb(new Uri(url).PathAndQuery);
  14.     }
  15.     if (!this.defaultWeb.Exists)
  16.     {
  17.         throw new ArgumentException(Resources.GetString("CannotFindWeb",
  18.                           new object[] { url }));
  19.     }
  20.     this.defaultWebUrl = this.defaultWeb.ServerRelativeUrl;
  21.     this.openedWebs = new Dictionary<string, SPWeb>();
  22.     this.openedWebs.Add(this.defaultWebUrl, this.defaultWeb);
  23. }

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.

  1. protected BaseRepository(string listName, string webUrl, bool readOnly, bool crossSite)
  2. {
  3.     ReadOnly = readOnly;
  4.     ListName = listName;
  5.     WebUrl = webUrl;
  6.  
  7.     var ctx = SPContext.Current;
  8.     IsAnonymous = ctx != null && SPContext.Current.Web.CurrentUser == null;
  9.     if (crossSite)
  10.     {
  11.         var backupCtx = HttpContext.Current;
  12.         // Reset the context
  13.         HttpContext.Current = null;
  14.         // Initialize a new context and others
  15.         InitializeParameters();
  16.         HttpContext.Current = backupCtx;
  17.     }
  18.     else
  19.     {
  20.         InitializeParameters();
  21.     }
  22. }

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:

  1. // Another site collection
  2. var repository1 = new EmployeeRepository("http://SharePointServer/linq"truetrue);
  3. var items1 = repository1.GetEntityCollection();
  4. foreach (var employee in items1)
  5. {
  6.     // Render information about an employee
  7.     RenderEmployeeInfo(employee);
  8. }
  9. Controls.Add(new LiteralControl("***<br/>"));
  10. // Current site collection
  11. var repository2 = new EmployeeRepository("http://SharePointServer/sites/asc"truefalse);
  12. var items2 = repository2.GetEntityCollection();
  13. foreach (var employee in items2)
  14. {
  15.     // Render information about an employee
  16.     RenderEmployeeInfo(employee);
  17. }

Result of code above looks like this:


Share

Comments