About a month ago, I was working on case which from code point of view was damn simple; just a HelloWorld Webpart. But things are not so easy if they appears to be. The issue was running one single line of code in a webpart constructor; which works perfectly fine if we add in CreateChildControls(). Issue was easily reproducible; as in Initial stage we couldn’t find why it happens; I seriously thought to say to the customer as what is such a business need that he need to write in constructor itself; as CreateChildControls() will also be called always. But as we say customers drives us. With help of my Escalation Engineer Andy Li, we found what was the reason. Before, I begin to drill down , few important points to note:
Note:
  • Please put the sample code in  the Constructor in Try…Catch block, else you won’t be able to add the webpart to the page.
  • It will throw ERROR: “Cannot import this webpart”. Similar to the image:
    Webpart Error

Scenario 1:
       Following error is generated from the given sample code:
ERROR:
at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
at System.Security.CodeAccessPermission.Demand()

at System.Environment.get_MachineName()
at HelloWorldWebpart.Class1..ctor()

public class Class1 : WebPart
   {
       private string name = "";
       public Class1()
       {
           try
           {
              name = Environment.MachineName;
           }
           catch (Exception e)
           {
               System.Web.HttpContext.Current.Response.Write(e.StackTrace);
           }
       }
       protected override void CreateChildControls()
       {
           base.CreateChildControls();
           Label label1 = new Label();
           label1.Text = name;
           Controls.Add(label1);
       }

Some Initial Conclusions: Just a vague idea, may be Environment.MachineName does not works with Web application (just meant for Windows Application ). There should be something in web application related stuff. Check Scenario 2.

Scenario 2:
       Following error is generated from the given sample code:
ERROR:
at System.Web.HttpServerUtility.get_MachineName() at HelloWorldWebpart.Class1..ctor()

public class Class1 : WebPart
    {
        private string name = "";
        public Class1()
        {
            try
            {
             name = System.Web.HttpContext.Current.Server.MachineName;
            }
            catch (Exception e)
            {
                System.Web.HttpContext.Current.Response.Write(e.StackTrace);
            }
        }
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            Label label1 = new Label();
            label1.Text = name;
            Controls.Add(label1);
        }
    }

Now, few tests to do before we jump on to root cause analysis:

  1. Is the error shown every time once you have added the webpart? Confused what's this question. Putting other way round, add the webpart either of Scenario1 OR Scenario 2, it throws error and along with that we can see the webpart added to the page.
    • Now, Exit the edit mode of the page. IS the error stack message still shown on the page? ::: NO
    • Is the output of the webpart rendered that is the machine name is visible? ::: YES
                         In short, error comes only once at the time of adding the webpart. Not after that.::: YES
  2. Try using the webpart given in Scenario1 OR Scenario 2 (without any modification)in the ASP.Net Webapplication. Does it WORKS? ::: YES. Any Error thrown?::: NO
  3. Remove the lines from constructor and add to CreateChildControls() method that is remove these lines from the respective Scenario code
    name = Environment.MachineName;
    name = System.Web.HttpContext.Current.Server.MachineName;
    and add these lines to respective CreateChildControls() method
    Does it WORKS? ::: YES. Any Error thrown?::: NO
  4. In the given sample code in Scenario 1, make the following modifications in the constructor and then check. Does it WORKS::: YES
    public Class1()
            {
                try
                {
                   EnvironmentPermission obj1 = new EnvironmentPermission(EnvironmentPermissionAccess.Read, "COMPUTERNAME");
                    obj1.Assert();
                    name = Environment.MachineName; 
                   obj1.Deny();
                }
                catch (Exception e)
                {
                    System.Web.HttpContext.Current.Response.Write(e.StackTrace);
                }
            }
  5. In the given sample code in Scenario 2, make the following modifications in the constructor and then check. Does it WORKS::: YES
    public Class1()
            {
                try
                {
                   AspNetHostingPermission obj1 = new AspNetHostingPermission(AspNetHostingPermissionLevel.Medium);
                    obj1.Assert();
                    name = System.Web.HttpContext.Current.Server.MachineName;
                   obj1.Deny();
                }
                catch (Exception e)
                {
                    System.Web.HttpContext.Current.Response.Write(e.StackTrace);
                }
            }

Few questions, from these tests:

  1. Why the error message comes just once and not always?
  2. Why the webpart works fine with ASP.Net Web application & not with SharePoint?
  3. Why removing the line from the constructor in the respective Scenario code and thereby adding the same line to CreateChildControls() worked? What was wrong in the constructor?
  4. We have turn on the trust level to "Full" in the web.config file, but still the code doesn't works?
  5. Why the code is considered as Partial Trusted code?
  6. The webpart is added to GAC which should mean “Full Trust” ; but still the code doesn't works.
  7. Why we require to add code to give explicit permissions for the lines in the constructor to work?

ROOT Cause Analysis:

  1. The issue is because of the way the Page Parsing functionality works in SharePoint. In SharePoint context, the Page Parsing is done by SPPageParseFilter.
    1. It ignores the security configuration (if any) & even if the DLL is in GAC. It expects the explicit permissions.
    2. It considers the DLL to be Partial Trusted code though you install the assembly in GAC
  2. Now, why it happens with the Constructor only?
    1. The Page Parser (SPPageParseFilter) only access the Constructor. It does not access any other method like CreateChildControls() and those methods will be called in the page rendering only.
    2. Now, the Page Parser calls the constructor and expects
      • The explicit security permission included.
      • The called assembly to allow Partially Trusted Callers.
      In case, not found throws the errors.
  3. The reason why we see the error message just once is the Parser calls the page only once and keeps in the cache. When we do not provide any explicit permissions (just added DLL to GAC and changed the trust level in the web.config file), the parser access the constructor for the first time, it throws the error. But when the page rendering happens it does not throws the error. In the successive calls the parser doesn't comes to the picture and only the page rendering mechanism happens and it doesn't have any issues in accessing the constructor or CreateChildControls ()
  4. Because of this behavior its always recommended to either:  (especially while calling methods in constructors)
    1. Explicitly provide the permissions
    2. Either make the caller assembly as Allow Partially Trusted Caller OR make a Wrapper round the called assembly and thereby expose the wrapper assembly as Allow Partially Trusted Caller. Now use the wrapper assembly.