LINQ to SharePoint and RunWithElevatedPrivileges

Recently one of my colleagues faced an issue with LINQ to WebPart while executing the code with elevated permission. It was a WebPart using LINQ to query a list called “Announcements” for which the currently logged on user does not (and should not) have access (therefore the web part runs with elevated privileges).But it was not working because LINQ seems to build some “context” even before the web part runs with elevated privileges so that it throws an exception says that list “Announcements” is not found.

Problematic code:

        private Dictionary<string, List<Annoucementlist>> GetAnnouncmentListInfo()

        {

            try

            {

                SPSecurity.RunWithElevatedPrivileges(delegate()

                {

                    using (SPSite sc = new SPSite(SPContext.Current.Site.Url))

                    {

                        using (DemoSiteContext dc = new DemoSiteContext (sc.RootWeb.Url))

                        {

                            var q = from annoucements in dc.Announcements // When the currently logged on user does not have permissions on the “Announcements ” list, this crashes

                                    where annoucements.Title == “something”

                                    select annoucements.Title;

                        }

                    }

                });

            }

            catch (Exception) // System.ArgumentException List " Announcements " not found!

            {

            }

            return functionalAreas;

        }

It was an interesting issue, after researching found that it was because of a constructor call in the class SPServerDataConnection class within the Microsoft.SharePoint.Linq.dll

Culprit: Microsoft.SharePoint.Linq.Provider.SPServerDataConnection class

Once we create a new instance of SharePoint Linq DataContext it actually executes a constructor of SPServerDataConnection class where some funny things happening in behind. If we are executing the code with SPContext then it will be using the SPSite and SPWeb instances under the current context and ie why we are getting the exception.

Here is the internal code of SPServerDataConnection that I got via Reflector.

public void 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);

}

Work around: we can tweak this functionality by creating a new HttpContext and use it under elevated permission to execute your code. After your usage set the actual SPContext back. (it is a work around by playing with HttpContext J so be careful )

Sample WebPart code given below

using System;

using System.ComponentModel;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using Microsoft.SharePoint;

using Microsoft.SharePoint.WebControls;

using Microsoft.SharePoint.Linq;

using System.Collections.Generic;

using System.Linq;

namespace LinqWebPart.WebPart1

{   

    [ToolboxItemAttribute(false)]

    public class WebPart1 : WebPart

    {

        protected override void CreateChildControls()

        {

            string strUrl = SPContext.Current.Web.Url;

            HttpContext backupCtxt = HttpContext.Current;

            try

            {

// if there is a SPContext make it is as null so LINQ won’t execute under current context

               if (SPContext.Current != null)

                    HttpContext.Current = null;

                SPSecurity.RunWithElevatedPrivileges(delegate()

                {

                    using (SPSite oSite = new SPSite(strUrl))

                    {

                        using (SPWeb oWeb = oSite.OpenWeb())

                        {

       // creating a new HttpContext under elevated permission and setting it as current control context web, because of this the code will be running under elevated permission.

                            HttpRequest httpRequest = new HttpRequest("", oWeb.Url, "");

                            HttpContext.Current = new HttpContext(httpRequest, new HttpResponse(new System.IO.StringWriter()));

                            SPControl.SetContextWeb(HttpContext.Current, oWeb);

                            using (DemositeDataContext dc = new DemositeDataContext(oWeb.Url))

                            {

                                var q = from list in dc.Announcements

                                        where list.Title == "My Announcment title"

                                        orderby list.Id

                                        select new { DisplayName = list.Title };

                                //remaining code goes here...

                            }

                        }

                    }

                }

               );

            }

            catch (Exception ex)

            {

                //Use your favourite form of logging to log the error message and exception ....

            }

            finally

            {

  // make sure that you are setting the actual SPContext back after your operation and make SharePoint happy J

                if (SPContext.Current != null)

                    HttpContext.Current = backupCtxt;

            }   

        }

    }

}

It is a known design behavior with LINQ to SharePoint.

Related information is documented in the following MSDN article: https://msdn.microsoft.com/en-us/library/ff798485.aspx

Using SPQuery with Regular SharePoint Lists

You should consider using the SPQuery class, instead of LINQ to SharePoint, in the following scenarios:

· When you have anonymous users on your site. LINQ to SharePoint does not support anonymous user access.

Note:

This limitation exists at the time of publication. However, it may be resolved in future service packs or cumulative updates

I have tested with Aug CU 2010 and it is not yet changed, it may change in future.