Saturday, February 20, 2010

SharePoint Page Code Behind - Reference to Assembly and Namespace

Yesterday someone asked me how to resolve the "assembly x could not be loaded" when using custom code behind to aspx pages in SharePoint. The trick was that the class namespace was different than the project name, the assembly was placed in the GAC, and I think the class was not declared as public partial.

So, this tutorial will quickly show you the steps to add a code behind to a custom SharePoint page and then how to modify the web.config and the page code to use the custom code. We will then change the namespace so that the project / assembly name is different to the namespace name and we will see the impact that has on SharePoint and how we should fix it.

Create a new sandbox SharePoint site and open the site in SharePoint Designer. (WSS 3 or MOSS 2007)
Start with a vanilla (unmodified) site to ensure that other problems dont impact the test.













In SharePoint Designer, locate the page which you want to add the code-behind to and open in Code-view. For this tutorial I selected the Default page and selected "Create New from Existing". I then saved my new page as CodeBehindPage.aspx and opened the page in code view.

Notice the top section of the page should look like:
<%@ Page language="C#" MasterPageFile="~masterurl/default.master"

Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint,
Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint,
Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

Switch to Design view and add a ASP.NET button to the form.
Change the ID of the button to cmdHello.
  
 
 
 
 
 
 
 
 
 
 
 
 Add a ASP.net Label to the form and change the ID to lblMessage.


 
 
 












Save the page and view the page in browser to ensure everything is working.
 
Now, lets add some custom code.
 
Start a new Visual Studio project.
I used VS 2008 and created a .Net 3.5 Class library project.

 
 
 
 
 
 
 
 
 
 
 
 
 
Rename Class1.cs to Messenger.cs
 
using System;

using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyHelloWorld
{
   public class Messenger
   {
   }
}



Add references to:
System.web
Microsoft.SharePoint
 
Add the following to your class:
using Microsoft.SharePoint;

using System.Web.UI;
using System.Web.UI.WebControls;

change:
public class Messenger
to
public partial class Messenger: System.Web.UI.Page

Add code to your class so that you have the following:
using System;

using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace MyHelloWorld
{
public partial class Messenger: System.Web.UI.Page
{
  protected Button cmdHello;
  protected Label lblMessage;
  protected override void OnInit(EventArgs e)
  {
   base.OnInit(e);
   cmdHello.Click += new EventHandler(cmdHello_Click);
  }

  protected void Page_Load(object sender, EventArgs e)
  {
   if (!IsPostBack)
   {
    lblMessage.Visible = false;
    cmdHello.Text = "Say Hello";
   }
  }

  protected override void OnLoad(EventArgs e)
  {
   base.OnLoad(e);
  }

  protected void cmdHello_Click(object sender, EventArgs e)
  {
   lblMessage.Text = "Hello";
   lblMessage.Visible = true;
  }
 }
} 

Sign assembly with strong-name-key

Build assembly

Copy assembly to C:\inetpub\wwwroot\wss\VirtualDirectories\80\bin where 80 is the port number of you web application.

OK, so now we have our page ready, we have our code ready, and we need to tell our custom page to use the new code instead of the native SharePoint code.
 In SharePoint Designer, open the page in code view and replace:
<%@ Page language="C#" MasterPageFile="~masterurl/default.master"
Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:webpartpageexpansion="full" meta:progid="SharePoint.WebPartPage.Document" %>
With:
<%@ Page language="C#" MasterPageFile="~masterurl/default.master" Inherits="MyHelloWorld.Messenger" meta:progid="SharePoint.WebPartPage.Document" %>

Find the public key token of your assembly. You can do this through Visual Studio or by using the RedGate Reflector tool.



















Add the following to the safecontrols section in the web.config file:
(the web.config file is located in C:\inetpub\wwwroot\wss\VirtualDirectories\80 where 80 is the port number of your web application)

<SafeControl Assembly="MyHelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0c51afea590bb15c" Namespace="MyHelloWorld" TypeName="*" Safe="True" AllowRemoteDesigner="True" />

Save the page and the web.config file and browse to the page.

You will see that the custom code now works !!




















Now the fun part...
What if the namespace in my class has a different name than the assembly... where do I use the assembly name vs. namespace name?
Go back to your code in Visual Studio and change the namespace in the class to MyHelloWorldfromCompanyX and rebuild the solution.

Copy the new assembly to C:\inetpub\wwwroot\wss\VirtualDirectories\80\bin and select to override the old one.

If you now try to browse to your custom SharePoint page you will get the following error:









So, to resolve this we need to open the custom SharePoint page in code view in SharePoint designer.

Then, change
<%@ Page language="C#" MasterPageFile="~masterurl/default.master" Inherits="MyHelloWorld.Messenger" meta:progid="SharePoint.WebPartPage.Document" %>

to
<%@ Page language="C#" MasterPageFile="~masterurl/default.master" Inherits="MyHelloWorldfromCompanyX.Messenger" meta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full" %>

Save the page and open the page in web browser...you can expect the following error:






So, we also need to change the namespace in the web config file

Change:
<SafeControl Assembly="MyHelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0c51afea590bb15c" Namespace="MyHelloWorld" TypeName="*" Safe="True" AllowRemoteDesigner="True" />
to
<SafeControl Assembly="MyHelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0c51afea590bb15c" Namespace="MyHelloWorldfromCompanyX" TypeName="*" Safe="True" AllowRemoteDesigner="True" />

Save the web.config file and refresh the browser window....and... wala...your custom code is working !!

Enjoy !!

9 comments:

Anonymous said...

I had to add the full assembly name in the CodeBehindPage.aspx to get it to work:

<%@ Page language="C#" MasterPageFile="~masterurl/default.master" Inherits="MyHelloWorld.Messenger, MyHelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0c51afea590bb15c" meta:progid="SharePoint.WebPartPage.Document" %>

Anonymous said...

This was a great tutorial. I got my custom DLL working. However, I'm having trouble with my DLL calling a third party DLL. I've been going crazy trying to get it to work. Any suggestions? I'm currently getting a "The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)" error when my code attempts to access the third party DLL.

Johan Olivier said...

Hi,
The 3rd-party dll problems could be one of many. You can try the following:

1-Make sure the 3rd-party dll is signed with a strong name key and placed in the GAC.
2-Register the assembly in your page directive.

To do this:
2.1 Open the custom page
2.2 add the following to the top of the page:
<%@ Assembly Name="3rdpartyassemblyname, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5eb2badc8b2dc926" %>

Remember to change the 3rdpartyassemblyname to that which you want to use as well as the publicKeyToken. (can get this from GAC)
The line above must be before the Page directive..(in other words, put it right at the top of the page)

<%@ Page language="C#" MasterPageFile="~masterurl/default.master" Inherits="ViewContract.ViewContract" meta:progid="SharePoint.WebPartPage.Document"%>

Johan Olivier said...

Also, theck out the following: http://www.techontour.com/sitenews/2007/11/02/fixing-the-given-assembly-name-or-codebase-was-invalid/

Anonymous said...

Thanks,

I am new to SharePoint and this is an excellent tutorial, worked perfectly.

But I have a problem when I try to pull data from a list.

I get an error in the following code;

SPSite site = SPContext.Current.Site;

What might be the issue?

Thanks again

Johan Olivier said...

Thanks for the complement! much appreciated.
I would like to look at the error which you receive when you execute the line:
SPSite site = SPContext.Current.Site;

Can you please email the complete code block and screenshot of the error to JohanOlivier.SharePoint@Gmail.com, or post the code as a comment on this blog.
thanks

sharepointCode.co.uk said...

Useful guide dude, and that SP Context error is a common one, try using SPSite("urlhere")...

Anonymous said...

Hi.

Nice article. It's exactly what I need to do and obviously seems to work for most people, but for some reason, it's not working for me.
The problem appears to be that the protected controls declared in the code-behind are not being wired to the controls on the .aspx page.
I carefully followed all your instructions and I know that the page is talking to the class because I get a NULL reference exception in OnInit() when trying to add the clicking handler to the Button control (which is NULL). The controls (a Button and a TextBox) have the same name as the controls on the .aspx page. If I init them, e.g., "protected Button wPB_SaveComment = new Button();", then I no longer get the NULL exception, but none of the properties I change in PageLoad() affect the controls on the .aspx page.

I'm using SharePoint 2010 Standard, the .aspx page is modified using SharePoint Designer 2010 and the class is developed and deployed (copied to C:\inetpub\wwwroot\wss\VirtualDirectories\80\bin) using Visual Studio 2010 Ultimate.
I'm sure this is something really basic, but I'd really appreciate any hints or ideas on how to get this going ASAP.

Thanks again for a great article.

vapcguy said...

Proper code is
using (SPSite site = new SPSite(SPContext.Current.Site.Url))
{
....
}

Post a Comment