LINQ to SharePoint – Scope is Site Collection

September 15 2010 9 comments

We made a discovery earlier this year when starting to develop our first customer projects on SharePoint 2010 that the DataContext does not extend over multiple site collections in SharePoint context, while it does so out of the mentioned context. This means you can’t develop customizations on SharePoint 2010 using LINQ to SharePoint over SPMetal generated DataContexts, if you want to retrieve data from multiple site collections without obscure workarounds. This is somewhat confusing, while an msdn article How to: Query Using LINQ to SharePoint suggests it’s possible as an example shows how to initialize DataContext from this.Site.WebApplication.Sites[0].RootWeb.Url and the quote goes: You can also use the context to get, indirectly, the URLs of other Web sites within the site collection, or even the other site collections of the Web application. Although the quote only references to getting URLs of sites from SPContext, the code sample illustrates how to use DataContext with those URLs (even the other site collections of the Web application).

However, if you’re testing your SPMetal driven data model with a console application – as I have – and have cross site collection content queries, you won’t end up with the limitation.

Here’s a little demonstration – a console app with an SPMetal generated data access to retrieve blog-posts from SharePoint sites.

First, we would generate data access model with SPMetal. In this case the objective is reached shown in the following image.

The result is a code file named BlogDC.cs, namespace would be DataModel.Blogs, not DataMode.Blogs, a typo in the screen shot.

Then a console app, which retrieves blog posts from given site collections using the generated data access and outputs the titles of the posts:

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

namespace DataModel.Blogs
{
    class Program
    {
        static void Main(string[] args)
        {
            var sites = new List<string> {"http://oob.sp.dev",
            "http://oob.sp.dev/sites/test/"};

            var posts = Get(5, sites);

            foreach (var post in posts)
            {
                Console.WriteLine(post.Title);
            }

            Console.ReadKey();
        }

        public static IEnumerable<Post> Get(int limit,
        List<string> siteUrls)
        {
            var posts = new List<Post>();
            foreach (var siteUrl in siteUrls)
            {
                posts.AddRange(Get(limit, siteUrl).ToList());
            }

            return posts.OrderByDescending(p => p.Published).Take(limit);
        }

        public static IEnumerable<Post> Get(int limit, string siteUrl)
        {
            var posts = new List<Post>();

            using (var site = new SPSite(siteUrl))
            {
                foreach (var web in site.AllWebs.Where(w =>
                w.WebTemplate.Equals(SPWebTemplate.WebTemplateBLOG)))
                {
                    posts.AddRange(GetFromWeb(limit, web.Url));
                }
            }

            return posts.OrderByDescending(p => p.Published).Take(limit);
        }

        public static List<Post> GetFromWeb(int limit, string webUrl)
        {
            using (var dc = new BlogDataContext(webUrl))
            {
                try
                {
                    return (from post in dc.Posts
                            orderby post.Published descending
                            select post).Take(limit).ToList();

                }
                catch (ArgumentException) { return new List<Post>(); }
            }
        }
    }
}

The outcome of the console application is that it finds and outputs titles of two blog-posts “Blog in another site collection” and “My first blog post” from two different Blog-sites in two different site collections: http://oob.sp.dev and http://oob.sp.dev/sites/test/ as I have two site collections in my web application and a Blog-site on both of them with one post each .

My former colleague Sami Poimala took a little time to investigate a problem we had in our SharePoint customization case where a similar scenario to what is described above was first tested successfully in a console app but the data access model wouldn’t work when testing on SharePoint and would only find items from the current site collection. The confusion having read the msdn-article How to: Query Using LINQ to SharePoint, which suggests there should be no problem, was almost unbearable. Here’s what Sami found out with a little help of reflector and google:

Firstly, there seems to be an assumption made in Microsoft.SharePoint.Linq.Provider.SPServerDataConnection constructor, which forces the use of SPContext.Current.Site when operating in SharePoint context.

Secondly, you can find Chun Liu’s great dive into LINQ to SharePoint, which explains the behaviour – a little quote from Liu’s post: So why SPServerDataConnection was designed to use SPContext.Current.Site, instead of using new SPSite(url) directly? Does it make sense? Well, at lease to me it does make sense because site collection should be a scope of custom queries. .

So, what is the conclusion. Site collections are boundaries and you shouldn’t consider showing content from another site collection – at least with LINQ to SharePoint and SPMetal driven solutions, and if a slight expression of irritation is allowed considering the mixed information available – because it does make sense to some.

Popularity: 6% [?]

9 comments to “LINQ to SharePoint – Scope is Site Collection”

  1. Roger says:

    If this is the case, at least put a flag in the constructor or something that lets you specifiy which to use. And if you are going to cross site collections, we should be prepared for security context changes as well I suppose. I think Liu’s post about “because site collection should be a scope of custom queries. ” meaning it shouldn’t be (typo) but this is because the security context cannot be assumed valid. I’d say as long as I can specifiy both, we are good.

  2. Juha Pitkänen says:

    Thanks for your comment. Security context shouldn’t be a problem because every query is run in the context of current user and no data is or at least shouldn’t be retrieved if current user has no privileges to the queried list whether the list lies in the current or some other site collection. That’s how, for instance, CrossListQueryCache’s GetSiteData-method works and it works just fine. I don’t think Liu had a typo in his post. It becomes obvious if you read the next chapter after the supposed typo.

  3. Dan says:

    I’m late to this party, but I’ll throw an opinion into the pot. Surely Liu’s “rule” that custom queries shouldn’t cross a site collection boundary is an arbitrary one. Why shouldn’t they? While it might be convenient for the SharePoint development team to come up with such arbitrary limitations, the simple fact is that the world doesn’t want to work that way. I can’t think of a client I’ve worked with over the past three or four years who didn’t have some requirement that involved data mashups of cross-site-collection data. Many times that’s because their SharePoint installations have evolved over time and just don’t have “optimal” site structures.

    While it might make someone feel important to declare that it shouldn’t be done because an obvious screw-up in the code prevents it, let them try talking these clients into tearing down and rebuilding their farms so site collection boundaries don’t have to be crossed.

    Microsoft’s left that gap to be filled by 3p SharePoint vendors for years, and now that they actually have a reasonable OOTB mechanism for doing it, they ship it hobbled. Strange, but not completely unexpected.

  4. Juha Pitkänen says:

    Thanks for the comment Dan. I feel your pain. If you are looking for alternatives, Camlex.NET (http://camlex.codeplex.com/) might be something to interest you. Haven’t tried it yet myself but have met Alexey Sadomov and am convinced.

  5. Kain says:

    I too worked under the assumption that Linq to SQL would be universal in its access given it is working with the server object model, and I only discovered the issue when trying to deploy a solution to the production environment.

    In my dev environment I had created a sub web off the Site root as a data site for shared data to simulate our production environment, but only realised when deploying that the live data site was in its own site collection, as it’s shared data is used by multiple site collections. Rather than rewriting all my code to dump Linq and use manual loading of sites/webs/lists I looked into overriding certain functions as a potential solution, but Microsoft have locked down most of the key areas with the “internal” keyword.

    Its a bit of a hack but I was able to get around this rather stupid (imo) limitation with a bit of reflector digging. Essentially I modified the getter of the lists in the generated data context classes to temporarily clear the HttpContext while doing the Linq requests, which force the SPServerDataConnection constructor to use my specified URL rather than the current context.

    public Microsoft.SharePoint.Linq.EntityList MyList
    {
    get
    {
    HttpContext current = HttpContext.Current;
    HttpContext.Current = null;
    EntityList items = this.GetList(“My List”);
    HttpContext.Current = current;
    return items;
    }
    }

    I’m not sure if there is any potentially implications of this that I have overlooked (potentially including multiple threads using the context singleton), but a cursory review of the code in reflector seems to indicate that there would be no obvious issues with this. If anyone can tell me differently I would be interested to hear your thoughts on it.

  6. Juha Alhojoki says:

    Yes, Kain, that’s the technique of the workaround. You might want to consider try {} finally {} when toggling HttpContext.Current.

  7. Hey guys,
    Great article. I am pretty new to SP so i decided to try out the Linq to SP in our multi-site implementation that I am working on. I quickly found this issue as well while reflecting via dotPeek. Did anyone submitted a feature request to take care of this? This seriously limits the usefullness of the provider and thus makes creating SP implementation so much harder.
    I am currently looking at a way to report this as a bug or get in touch with SP dev team. Ping me if you’re interested on the outcome.
    PS: i have not used the blog site in a while but does have current info to get to me.

  8. Mi Programador está tratando de persuadir a convencer a mí para pasar a .net desde
    PHP. Siempre me ha disgustado la idea debido a los gastos costes.
    Pero él no tryiong menos. He estado usando WordPress del diversas páginas web por alrededor de un año y estoy nerviosa preocupados en cambiar a otra plataforma.

    He oído grandes. ¿Hay una manera que puedo
    transferencia toda mi wordpress Mensajes en él? Cualquier ayuda sería
    muy grandemente apreciada!

  9. Now I am going to do my breakfast, after having my breakfast coming yet again 6
    minutes to skinny reviews (http://Www.docspal.Com)
    read more news.

Leave a Reply