Monday, July 26, 2010

Adding a gallery to your .NET 4.0 application

After a frustrated user posted his troubles getting GSP to work in a .NET 4.0 application, I thought I would dig into it to see what was going on. I have step by step directions for Visual Studio 2008 in the Admin Guide, but it turns out they don’t quite work for Visual Studio 2010. I also felt I could improve upon the explanation that is in the Admin Guide, so I decided to completely rewrite it in this blog post. Eventually I will update the Admin Guide to include this information.

I will create a default .NET 4.0 web application and then add a gallery to it. These steps are largely the same for Visual Studio 2005 and 2008, too.

Download the sample application I created for this post. A couple notes: (1) Log in to the gallery with username Admin (password=111). (2) For demonstration purposes, it includes the latest release of the AJAX Control Toolkit, not the version that ships with GSP.

Integration strategies

First, let’s review the ways one can add a gallery into an existing ASP.NET app, in order from least coupled to most coupled:

1. Deploy the GSP support files and DLLs and don’t use assembly references – The contents of the gs directory are deployed to your server but not included as content files in your Visual Studio project. The GSP assemblies are deployed to the bin directory but not referenced by the project. The only parts of your project that are “aware” of GSP are: (1) App_GlobalResources contains the resource file GalleryServerPro.resx, (2) web.config contains the required GSP configuration, (3) the web page for the gallery contains a Register tag for the GSP user control and, of course, the Gallery user control itself.

The advantage of this approach are:

  1. You can add a gallery to your site without having to recompile it.
  2. You can update to new versions of GSP without having to recompile – just copy the new files to your server.
  3. Your Visual Studio project has minimal knowledge of GSP, which helps to keep things clean and allows you to focus on your application without thinking too much about GSP.

The disadvantages of this approach are:

  1. You cannot use the GSP API (which may be used in advanced customization scenarios).
  2. You no longer have your entire app contained within a single project, which makes management more difficult.
  3. You cannot use the built-in publishing functionality in Visual Studio to deploy your app. Well, not the GSP portion anyway.

This is the approach I use for the demo gallery on www.galleryserverpro.com.

2. Include the files as part of the Visual Studio solution, but don’t reference the GSP DLLs – Copy the gs directory and other files into your app and make them part of your solution/project. This is the method documented in the Admin Guide. It has the advantage of keeping everything organized in one project, which allows for simple source control and deployment, but tends to clutter up your app with GSP dependencies that can be a distraction.

3. Same as above, but also reference the GSP DLLs - Copy the files into your app and make them part of your solution/project. Add references to the GSP assemblies to make your app aware of GSP. This is required when you use the API in advanced scenarios (for example, if you want to create your own upload page and add media objects yourself). But doing this in .NET versions higher than 2.0 will often give you compile errors. They are all solvable without too much work, but they can be a hassle.

Step by step: Adding a gallery

Each strategy can be the right solution depending on your requirements. Unless you intend to use the GSP API, most developers will want to use the first or second technique.

Here are step by step directions for adding a gallery to an existing .NET 4.0 application using the first strategy. In this example, I’ll use Visual Studio 2010 Ultimate to add a gallery to a default ASP.NET application. Just for fun, I am using a Visual Basic project, which works great even though GSP is written in C#. I believe the steps are the same for all web-enabled versions of Visual Studio, including the free Express one.

Note: By default, Visual Studio creates pages that require compilation, but at the end of this post I’ll show how to create pages that don’t require compilation, thus providing you with the no-compile advantage I mentioned above.

Note: If you already have an existing web app, skip to step # 3.

  1. Use Visual Studio 2010 to create a new ASP.NET 4.0 application using the ASP.NET Web Application template. Here I am using Visual Basic, but you can use another language.

    dotnet4_1

  2. The project will be created. In Solution Explorer, right click Default.aspx and choose View in Browser. The home page appears:

    dotnet4_2

  3. Okay! We have a working ASP.NET 4.0 app. Now we want to add a gallery to it. If you do not have an App_GlobalResources directory in your application, create one by using Visual Studio to right click the web project node in the Solution Explorer and choosing Add - Add ASP.NET Folder - App_GlobalResources. Similarly, add an App_Data folder if your project does not have one.

  4. Download the compiled version of Gallery Server Pro and extract the contents to a temporary directory. Use Windows Explorer to copy the following items:

    1. Copy the gs directory into your ASP.NET app. It should be at the same level as your other top-level directories, such as App_Data.
    2. Copy the contents of the bin directory into your bin directory.
    3. Copy GalleryServerPro.resx from App_GlobalResources into the same directory in your app.
    4. Copy galleryserverpro_data.sqlite from App_Data into the same directory in your app (not necessary if you use SQL Server).
    5. Copy web_4.0.config into the root of your app. Delete the existing web.config and rename web_4.0.config to web.config. NOTE: Your web.config may have settings you need to preserve, so you may need to manually merge the two instead.
  5. Create a new web page to hold the gallery. In this example, I created a page named Gallery.aspx from the Web Form Using Master Page template and then selected Site.Master for the master page. At the top of the page add a line to tell the page where the Gallery user control is defined:

    <%@ Register TagPrefix="gsp" Namespace="GalleryServerPro.Web" Assembly="GalleryServerPro.Web" %>

  6. Then add the user control somewhere in the body of the page:

    <gsp:Gallery ID="g" runat="server" />

  7. Compile the application. You will probably get the compilation error “Type ‘GalleryServerPro.Web.Gallery’ is not defined”, as seen here:

    dotnet4_3

  8. This error is not covered in the Admin Guide because it never happened under Visual Studio 2008. The reason for this error is because Visual Studio added a definition for the control in the page’s designer file and the compiler doesn’t know where the user control is defined. This doesn’t seem right to me - I would argue that it should know where it is because we explicitly defined it in the <%@ Register … /> tag.

  9. But no matter, because we can easily fix it. Double click the error to show the offending code:

    dotnet4_4

  10. Delete the line that is causing the error. As best I can tell there are no harmful consequences to removing it. The page will look like this when you are done:

    dotnet4_5

  11. Now compile the application. It should succeed. Right click the page and choose View in Browser. The page will appear with an empty gallery:

    dotnet4_6

  12. By default, no users are defined and the gallery is configured to use SQLite for data storage, so run the install wizard to set up the membership system and optionally configure SQL Server. In the web browser, add “?g=install” to the URL. The install wizard appears:

    dotnet4_7

  13. Step through the install wizard. When complete, you are redirected back to your gallery, which now includes a sample album and image. All done!

    dotnet4_8

Integrating with existing users

The integration example above assumes you are setting up a new set of users, but you might want the gallery to be integrated with your existing membership provider. After running the install wizard, open web.config and update the membership, role, and profile sections to point to the desired providers. GSP will use the default provider unless you specify a particular provider in the galleryserverpro.config settings membershipProviderName and roleProviderName.

After pointing web.config to your membership, you may get the following error:

dotnet4_9

This is happening because GSP doesn’t know what kind of security access any of your users have, so it assumes a “better safe than sorry” position and doesn’t let anybody in. To define the administrator, open the table gs_Role. GSP should have created a list of records in this table that mirror the roles in your role provider (if you don’t see any, try restarting the web app, which triggers the validation code that synchronizes the roles in gs_Role with the roles in your role provider). Pick the record that represents the system administrator role and set the AllowAdministerSite column to true. Then restart the app (or edit the album summary, which clears the role cache). Any users in this role should now have full administrative permission to the gallery. Now that you can log on as an administrator, you can use the normal GSP admin functions to define the security for the rest of the users.

No compile option

To add a gallery to your app without requiring compilation, remove the code behind file from the web page that contains the gallery user control. For example, in the example above, change the first line from this:

<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" CodeBehind="Gallery.aspx.vb" Inherits="WebWithGsp.Gallery" %>

to this:

<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/Site.Master" %>

After this change, the code behind files Gallery.aspx.vb and Gallery.aspx.designer.vb are no longer need and can be deleted.

An added benefit to this option is that you won’t be affected by the compile error we received above, since the designer file does not exist.

ASP.NET compatibility

Update 2010-08-03: Check out this blog post for more info.

If you look carefully at the screen shots, you will notice we lost the nice formatting of the menu after we added the gallery:

dotnet4_10

This is because the 4.0 version of web.config that ships with GSP is configured to render objects the pre-4.0 way. That is, the attribute controlRenderingCompatibilityVersion is set to 3.5:

<pages theme="" styleSheetTheme="" validateRequest="false" controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" />

This is an application wide setting, so this causes the menu to render using tables (<table>) rather than list items (<li>), which breaks the CSS in the default ASP.NET application.

You can restore the formatting of the menu by setting the compatibility version to 4.0, or removing it altogether. After this change, it looks like this:

dotnet4_11

I specified 3.5 compatibility mode in web.config because I wanted to guarantee that everything worked the same in .NET 4.0. I am not sure it is required, though. In some very brief testing, I couldn’t find anything that was broken in 4.0 mode. It is possible that future releases will default to the native 4.0 rendering.

AJAX Control Toolkit

GSP ships with version 1.0.10920 of the AJAX Control Toolkit, which is the most recent version of the toolkit that is compatible with .NET 2.0. If you want to use later versions in your app, GSP will work with it, but you must update web.config so that GSP uses the desired version rather than looking in vain for the one it was compiled against. Open web.config and add this line just before the final </configuration> element:

<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="AjaxControlToolkit" publicKeyToken="28f01b0e84b6d53e" />
      <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="4.1.40412.0" />
     </dependentAssembly>
  </assemblyBinding>
</runtime>

Update the newVersion attribute as needed to reflect the version of your AjaxControlToolkit.dll.

MVC apps

GSP is a webforms application, which is very different than the MVC pattern recently introduced by Microsoft. Generally speaking, webforms and MVC functionality don’t belong in the same application and can give you trouble if you try.

Having said that, it *is* possible to add a gallery to an MVC app. I actually did it for a customer not long ago. Here are the key lessons I learned:

  • The gallery user control must be added to a webforms page, not an MVC one.

  • I added a line to global.asax to tell the routing system to ignore paths to the webforms page. In my case, I put the webform (an .aspx page containing the Gallery user control) in a directory named Webforms and then added this line to global.asax: routes.IgnoreRoute("Webforms/{*pathInfo}");

  • The javascript on the Rearrange objects page failed until I set controlRenderingCompatibilityVersion to “3.5” in web.config.

  • Getting MVC and webform pages to share the same master page is difficult. I actually had to define a top level master page Root.Master that inherited from System.Web.UI.MasterPage. In this page I put the main layout. Then I created two child master pages – one was called Mvc.Master and it inherited System.Web.Mvc.ViewMasterPage; the other was called Webform.Master and it inherited from System.Web.UI.MasterPage. Both child pages used Root.Master as its own master page, and served as essentially empty containers for the web pages. All MVC pages used Mvc.Master; webforms used Webform.Master. A big problem with this approach is that the top level master page cannot use any MVC code. And if you add something to Mvc.Master, you must duplicate it in Webform.Master. This is a fragile approach that might create maintainability issues down the road, but I couldn’t come up with anything better. An alternative was to create identical master pages – one for MVC and one for webforms, but that is obviously not ideal.

6 comments:

Alexander said...

Hey Roger,
Thanks a lot for this post. I'm struggling exactly with this issue for the last two days, and this clarifies a LOT, about the three different ways to integrate gsp! If you add this later to the admin guide it would make it even better ;)
greets

Matt Dunn said...

Hi Roger,

I'm new to GSP. This compiled with VS2010 but I am getting a script error when clicking on any image in the sample gallery.

Line: 155
Error: 'AjaxControlToolkit' is undefined

Any idea? Thanks!

Roger Martin said...

Not sure why you are getting that error. I just downloaded the app, ran it, and was able to browse and add objects without trouble.

ML Stockell said...

Hi Roger,
When integrating into a site that already has users/roles/profiles in its database, I run into an issue where the user may have a role that is not registered in GSP. An exception is thrown by IGalleryServerRoleCollection. Should I just comment out
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Could not find a Gallery Server role named '{0}'. Verify the data table contains a record for this role and the current gallery, and that the cache is being properly managed.", roleName));


??

Roger Martin said...

ML Stockell: Restarting the web app should synchronize the aspnet_roles table with the gs_Role table. I have a fix to help prevent this in the upcoming 2.4 release.

MichaelCaditz said...

I'm adding GSP to an existing VS.net 2010 WAP. I'm having trouble with all of the XAML files in the mediaplayer folder.

The statements Storyboard x:Name produces the error: All objects added to an IDictionary must have a Key attribute or some other type of key associated with them. I fix this by changing the statement to Storyboard x:Key.


I am also getting a buid error: The tag VideoBrush does not exist in XML namespace . . .. I have no solution.