MVC Static Content DNS Helper

on Thursday, April 30, 2009

A website I was working on needed a way to include references to static content (images/css/js) which could dynamically point to the correct server for the build environment (dev,test,prod). This was accomplished by creating extension methods for the HtmlHelper.

I recently wrote about using a static content website on a previous post.

We already had an enumeration setup to describe the environment (listed below the post). We just needed a way to parse string values and add the dns name. To do this, we made a class for ASP.NET 2.0 websites and for ASP.NET MVC websites. It also needed to add the “.min.js” extension for production javascript.

ASP.NET 2.0:

using System.Web;
using Ucsb.Sa.Registrar.Util;

namespace Ucsb.Sa.Enterprise.Utility.Web
{
/// <summary>
/// Static content extension methods for <see cref="HtmlHelper" />. This file requires that the web
/// application contains a static AppSettings file with property BuildEnvironment.
/// </summary>
public static class StaticContentExtensions
{

#region variables

private const string BaseImageUrlFormat = "{0}://static.{1}sa.ucsb.edu/{2}";
private const string BasePlaceholder = "~";
private const string Image = "images";
private const string Css = "css";
private const string Js = "js";
private const string JQuery = "js/jquery";

#endregion

#region properties

/// <summary>
/// Gets and Sets the <see cref="BuildEnvironmentType" /> for the application. This
/// should be set before the first call to these extension methods.
/// </summary>
public static BuildEnvironmentType BuildEnvironment { get; set; }

/// <summary>
/// http or https
/// </summary>
public static string Scheme
{
get { return HttpContext.Current.Request.Url.Scheme; }
}

#endregion

#region public methods

/// <summary>
/// Converts an ASP url (eg. &quot;~/insideSa/bottom_header.jpg&quot;)
/// into a full url (eg. &quot;http://static.dev.sa.ucsb.edu/images/insideSa/bottom_header.jpg&quot;).
/// </summary>
/// <param name="partialUrl">The folder and file name under the static images directory.</param>
/// <returns>The full path to the static website with the sub-folder and file name appended.</returns>
public static string GetImageUrl( string partialUrl )
{
return GetUrl( partialUrl, Image );
}

/// <summary>
/// Converts an ASP url (eg. &quot;~/insideSa/site.css&quot;)
/// into a full url (eg. &quot;http://static.dev.sa.ucsb.edu/images/insideSa/site.css&quot;).
/// </summary>
/// <param name="partialUrl">The folder and file name under the static images directory.</param>
/// <returns>The full path to the static website with the sub-folder and file name appended.</returns>
public static string GetCssUrl( string partialUrl )
{
return GetUrl( partialUrl, Css );
}

/// <summary>
/// Converts an ASP url (eg. &quot;~/insideSa/jquery.js&quot;)
/// into a full url (eg. &quot;http://static.dev.sa.ucsb.edu/images/insideSa/jquery.js&quot;).
/// </summary>
/// <param name="partialUrl">The folder and file name under the static images directory.</param>
/// <returns>The full path to the static website with the sub-folder and file name appended.</returns>
public static string GetJsUrl( string partialUrl )
{
var url = GetUrl( partialUrl, Js );
return RewriteForMinJs( url );

}

/// <summary>
/// Converts an ASP url (eg. &quot;~/insideSa/jquery.js&quot;)
/// into a full url (eg. &quot;http://static.dev.sa.ucsb.edu/images/insideSa/jquery.js&quot;).
/// </summary>
/// <param name="partialUrl">The folder and file name under the static images directory.</param>
/// <returns>The full path to the static website with the sub-folder and file name appended.</returns>
public static string GetJQueryUrl( string partialUrl )
{
var url = GetUrl( partialUrl, JQuery );
return RewriteForMinJs( url );
}

/// <summary>
/// Creates the javascript code to load javascript libraries from google.
/// This will ensure that the uncompressed version of the code is loaded on the developer
/// systems.
/// </summary>
/// <param name="package">The name of the javascript library to load.</param>
/// <param name="version">The version number to retrieve.</param>
/// <returns>The string to be injected into a &lt;script&gt; section.</returns>
public static string GetGoogleLoad( string package, string version )
{
const string format = "google.load('{0}', '{1}'{2}";
const string compressed = ");";
const string uncompressed = ", {uncompressed:true});";

return string.Format(
format,
package,
version,
BuildEnvironment == BuildEnvironmentType.Local ? uncompressed : compressed
);
}

#endregion

#region private methods

private static string GetUrl( string partialUrl, string subDirectory )
{
var baseUrl = string.Format( BaseImageUrlFormat, Scheme, GetEnvironmentString(), subDirectory );
return partialUrl.Replace( BasePlaceholder, baseUrl );
}

private static string GetEnvironmentString()
{
var env = string.Empty; // default value for pilot and production

switch( BuildEnvironment )
{
case BuildEnvironmentType.Local:
case BuildEnvironmentType.Development:
case BuildEnvironmentType.Integration:
env = "dev.";
break;

case BuildEnvironmentType.Test:
env = "test.";
break;
}

return env;
}

private static string RewriteForMinJs( string url )
{
if( BuildEnvironment != BuildEnvironmentType.Production ) return url;
if( string.IsNullOrEmpty( url ) ) return url;

var len = url.Length;
if( len < 3 ) return url;

const string ext = ".js";
if( url.Substring( len - 3 ) != ext ) return url;

const string minExt = ".min.js";
if( url.Substring( len - 7 ) == minExt ) return url;

const string format = "{0}.min.js";
return string.Format( format, url.Substring( 0, len - 3 ) );
}

#endregion

}
}


And the corresponding ASP.NET MVC wrapper:



using System.Web.Mvc;
using Ucsb.Sa.Registrar.Util;

namespace Ucsb.Sa.Enterprise.Utility.Web.Mvc
{
/// <summary>
/// Static content extension methods for <see cref="HtmlHelper" />. This file requires that the web
/// application contains a static AppSettings file with property BuildEnvironment.
/// </summary>
public static class HtmlHelperStaticContentExtensions
{

#region properties

/// <summary>
/// Gets and Sets the <see cref="BuildEnvironmentType" /> for the application. This
/// should be set before the first call to these extension methods.
/// </summary>
public static BuildEnvironmentType BuildEnvironment
{
get { return StaticContentExtensions.BuildEnvironment; }
set { StaticContentExtensions.BuildEnvironment = value; }
}

#endregion

#region public methods

/// <summary>
/// Converts an ASP url (eg. &quot;~/insideSa/bottom_header.jpg&quot;)
/// into a full url (eg. &quot;http://static.dev.sa.ucsb.edu/images/insideSa/bottom_header.jpg&quot;).
/// </summary>
/// <param name="html">The object to add this extension method to.</param>
/// <param name="partialUrl">The folder and file name under the static images directory.</param>
/// <returns>The full path to the static website with the sub-folder and file name appended.</returns>
public static string GetImageUrl( this HtmlHelper html, string partialUrl )
{
return StaticContentExtensions.GetImageUrl( partialUrl );
}

/// <summary>
/// Converts an ASP url (eg. &quot;~/insideSa/site.css&quot;)
/// into a full url (eg. &quot;http://static.dev.sa.ucsb.edu/images/insideSa/site.css&quot;).
/// </summary>
/// <param name="html">The object to add this extension method to.</param>
/// <param name="partialUrl">The folder and file name under the static images directory.</param>
/// <returns>The full path to the static website with the sub-folder and file name appended.</returns>
public static string GetCssUrl( this HtmlHelper html, string partialUrl )
{
return StaticContentExtensions.GetCssUrl( partialUrl );
}

/// <summary>
/// Converts an ASP url (eg. &quot;~/insideSa/jquery.js&quot;)
/// into a full url (eg. &quot;http://static.dev.sa.ucsb.edu/images/insideSa/jquery.js&quot;).
/// </summary>
/// <param name="html">The object to add this extension method to.</param>
/// <param name="partialUrl">The folder and file name under the static images directory.</param>
/// <returns>The full path to the static website with the sub-folder and file name appended.</returns>
public static string GetJsUrl( this HtmlHelper html, string partialUrl )
{
return StaticContentExtensions.GetJsUrl( partialUrl );
}

/// <summary>
/// Converts an ASP url (eg. &quot;~/insideSa/jquery.js&quot;)
/// into a full url (eg. &quot;http://static.dev.sa.ucsb.edu/images/insideSa/jquery.js&quot;).
/// </summary>
/// <param name="html">The object to add this extension method to.</param>
/// <param name="partialUrl">The folder and file name under the static images directory.</param>
/// <returns>The full path to the static website with the sub-folder and file name appended.</returns>
public static string GetJQueryUrl( this HtmlHelper html, string partialUrl )
{
return StaticContentExtensions.GetJQueryUrl( partialUrl );
}

/// <summary>
/// Creates the javascript code to load javascript libraries from google.
/// This will ensure that the uncompressed version of the code is loaded on the developer
/// systems.
/// </summary>
/// <param name="html">The object to add this extension method to.</param>
/// <param name="package">The name of the javascript library to load.</param>
/// <param name="version">The version number to retrieve.</param>
/// <returns>The string to be injected into a &lt;script&gt; section.</returns>
public static string GetGoogleLoad( this HtmlHelper html, string package, string version )
{
return StaticContentExtensions.GetGoogleLoad( package, version );
}

#endregion

}
}


Then we used the static classes in a PartialView for rendering:

<%@ Import Namespace="Ucsb.Sa.Enterprise.Utility.Web.Mvc"%>
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<link href="<%= Html.GetCssUrl( "~/reset.css" ) %>" rel="stylesheet" type="text/css" media="screen" />
<link href="<%= Html.GetCssUrl( "~/blueprint/0_8/ie.css" ) %>" rel="stylesheet" type="text/css" media="screen" />
<link href="<%= Html.GetCssUrl( "~/blueprint/0_8/screen.css" ) %>" rel="stylesheet" type="text/css" media="screen" />
<link href="<%= Html.GetCssUrl( "~/blueprint/0_8/print.css" ) %>" rel="stylesheet" type="text/css" media="print" />
<link href="<%= Html.GetJQueryUrl( "~/1_3_2/themes/ucsb/ui.base.css" ) %>" rel="stylesheet" type="text/css" media="screen" />
<link href="<%= Html.GetJQueryUrl( "~/1_3_2/themes/ucsb/jquery-ui-1.7.1.custom.css" ) %>" rel="stylesheet" type="text/css" media="screen" />
<link href="<%= Html.GetJQueryUrl( "~/dataTables-1_4/css/datatables.css" ) %>" rel="stylesheet" type="text/css" media="screen" />
<link href="<%= Html.GetJQueryUrl( "~/mbMenu-2_2/css/menu.css" ) %>" rel="stylesheet" type="text/css" media="screen" />
<link href="<%= Html.GetCssUrl( "~/InsideSa/InsideSaLayout-0.0.0.1.css" ) %>" rel="Stylesheet" type="text/css" media="screen" />


Which generated html that looked like this:

<link href="http://static.dev.sa.ucsb.edu/css/reset.css" rel="stylesheet" type="text/css" media="screen" />
<link href="http://static.dev.sa.ucsb.edu/css/blueprint/0_8/ie.css" rel="stylesheet" type="text/css" media="screen" />
<link href="http://static.dev.sa.ucsb.edu/css/blueprint/0_8/screen.css" rel="stylesheet" type="text/css" media="screen" />
<link href="http://static.dev.sa.ucsb.edu/css/blueprint/0_8/print.css" rel="stylesheet" type="text/css" media="print" />
<link href="http://static.dev.sa.ucsb.edu/js/jquery/1_3_2/themes/ucsb/ui.base.css" rel="stylesheet" type="text/css" media="screen" />
<link href="http://static.dev.sa.ucsb.edu/js/jquery/1_3_2/themes/ucsb/jquery-ui-1.7.1.custom.css" rel="stylesheet" type="text/css" media="screen" />

<link href="http://static.dev.sa.ucsb.edu/js/jquery/dataTables-1_4/css/datatables.css" rel="stylesheet" type="text/css" media="screen" />
<link href="http://static.dev.sa.ucsb.edu/js/jquery/mbMenu-2_2/css/menu.css" rel="stylesheet" type="text/css" media="screen" />
<link href="http://static.dev.sa.ucsb.edu/css/insideSa/InsideSaLayout-0.0.0.1.css" rel="Stylesheet" type="text/css" media="screen" />


Here is the code for the environment variables:

namespace Ucsb.Sa.Registrar.Util
{
/// <summary>
/// Enumeration for each build environment. eg. Development, Test, etc.
/// </summary>
public enum BuildEnvironmentType
{
/// <summary>
/// local, localhost
/// </summary>
Local,

/// <summary>
/// dev, development
/// </summary>
Development,

/// <summary>
/// int, integration
/// </summary>
Integration,

/// <summary>
/// test, qa, quality assurance
/// </summary>
Test,

/// <summary>
/// pilot
/// </summary>
Pilot,

/// <summary>
/// prod, production
/// </summary>
Production
}
}


using System;

namespace Ucsb.Sa.Registrar.Util
{
/// <summary>
/// Attempts to parse string values to BuildEnvironmentType Enumerations.
/// </summary>
public static class BuildEnvironmentTypeParser
{

/// <summary>
/// <para>
/// Parses a string value into the corresponding environment type. Some partial and exact matches can be parsed.
/// </para>
/// <para>ie. Development, dev, prod, test, Production, etc.</para>
/// </summary>
/// <param name="environmentName">string to parse</param>
/// <returns>BuildEnvironmentType from parsed string.</returns>
/// <exception cref="ArgumentException">If the argument is unknown.</exception>
public static BuildEnvironmentType Parse( string environmentName ) {
if( string.IsNullOrEmpty( environmentName ) )
throw new ArgumentException( "Argument environmentName was null or empty." );

switch( environmentName.ToLower().Trim() ) {
case "local": case "localhost":
return BuildEnvironmentType.Local;

case "dev": case "development":
return BuildEnvironmentType.Development;

case "int": case "integration":
return BuildEnvironmentType.Integration;

case "qa": case "quality assurance": case "test":
return BuildEnvironmentType.Test;

case "pilot":
return BuildEnvironmentType.Pilot;

case "prod": case "production":
return BuildEnvironmentType.Production;
}

throw new ArgumentException( "Argument environment name, " + environmentName + ", is of an unknown type." );
}

}
}

Development Guide: UCSB static content website

(This entry has to do with my personal opinion about the way a static website should be built at UCSB and does not reflect the opinions of UCSB)

A static content website at UCSB would help with performance for a couple of the “high visibility” websites. But, it is not required. A couple of reasons why to use a static content website are described by YSlow (a plugin for Firefox): http://developer.yahoo.com/performance/rules.htm

At UCSB we should/will be creating a static content website for a couple more reasons:

  1. We copy a lot of static content into multiple of our websites (mostly for MasterPages). This causes:
    1. Duplicate content in the source control system.
    2. Maintenance issues when updating websites.
    3. Excessive copy times when deploying websites (well, this may not be a very big issue).
  2. A common infrastructure to look for images that are copyrighted/available for use on SA websites.

It seems that there is a need for all the developers to be on the same page when using a communal share for website static content. And in that vein here are a few Development Guidelines.

Development Guidelines:

  1. The static content website urls are at:
    1. Dev: http://static.dev.sa.ucsb.edu/ (only available within the SA network)
    2. QA/Test: http://static.test.sa.ucsb.edu/ (only available to UCSB IPs)
    3. Prod: http://static.sa.ucsb.edu/ (public)
  2. The base folder (/) should have sub-directories for common groupings. A few upfront common groupings are:
    1. js
    2. css
    3. images
  3. Any commonly used js/css/images should be kept under the appropriate subfolder.
  4. Any project specific js/css/images should be kept under a subfolder for the application.
    1. Examples: js/InsideSa, css/AAA
    2. If the website name is already taken, please use a new and appropriate name which describes the website the content is for. No restrictions, just don’t duplicate.
  5. For third party components (eg: jquery) place a parent directory under the appropriate top directory (eg: js/jquery), but then place version directories underneath it.
    1. js/jquery/1_2_6
    2. js/jquery/1_3_2
  6. For third party components to third party compents (eg: jqGrid) place a parent directory under appropriate third party diretory (eg: js/jquery/jqGrid), but then place version directories underneath it.
    1. js/jquery/jqGrid/1_4_3
  7. Never allow dots (.) in a directory name before the static content. I haven’t found any official documentation on it, but it seems like IIS will stop looking for sub-directories once the first dot is found.
    1. For Example: If you have /js/jquery/1.2.6/jquery-1.2.6-min.js, then IIS will return a “404 File Not Found” error message for file “1.2.6”; because it considers the first dot name (1.2.6: a directory) to be the name of the file.
    2. Please use underscores (_) in place of dots in directory names.
  8. When putting content into the static website always add a Version Stamp to the filename.
    1. CSS:
      1. Use a version number.
        1. Please use the same version number of your website update as the filename addition. For example: css/insideSa/InsideSaLayout-0.0.0.1.css
        2. ** alternative suggestion
    2. Javascript:
      1. Use a version number.
        1. Please use the same version number of your website update as the filename addition. For example: js/InsideSa/insideSa.menu-0.0.0.1.js
        2. ** alternative suggestion
    3. Images:
      1. Use a date/time stamp.
        1. Use the format <image name>_yyyyMMddHHmm.<ext>. For example: images/InsideSa/background_200903260203.jpg
        2. A date/time is used because multiple images with the same name can be created during development.
  9. Because versioning is done in the filename, you don’t have to delete any files. Please don’t delete any files in the “common area” of the folders. The project specific folders can have files deleted if the developer(s) feel it’s okay; but we strongly suggest you don’t. Just incase some unknown project is referencing the static content.
    1. Don’t worry about the disk space, it really shouldn’t eat up too much room. And if it becomes a problem we’ll work with the systems team to come up with a good solution.
  10. Javascript files should have a development version and a minified version (for production).
    1. The development (human readable) version should be formatted like the example in section 8.2.
    2. The minified version should have the same format as section 8.2 but with the extension .min.js.
      1. This means that sometimes you will have to change the names of javscript files you download. For example, Microsoft uses .js for the minified version and .debug.js for the development version.
      2. You can use jsmin.exe to minify your javascript files after development has finished.
      3. Before and after you use jsmin.exe you should run your file through jslint. jslint has a yahoo widget for it. If anyone knows of a google gadget for it, let me know. Or even a command line version, so we can add it to an automated build script.
    3. Mozilla has a great list of links for standard javascript tools.
  11. New Common sub-groupings do not need to be vetted (aka, submitted for approval), just make sure that there is no appropriate sub-directory already in existence.
    1. For example: RadControls. They require that the base sub-directory be the root for all js/css/images used within their Q3 2006 dll. (This has not been done yet, because RadControls integration with MVC is not “great” yet.)
      1. Sidenote to Telerik: We really liked your Blue theme from your 2006 components. But, those Blue themes were not added to your dll’s when you started compiling your resources into them. Which makes it difficult to easily upgrade from your website based components to your web application based dlls. =(
  12. The dev area will be hooked up to the source control system.
    1. The dev area will be automatically updated into the source control system on Friday.
    2. Please update your changes into the source control system before Friday when you see fit.
  13. (UPDATE) Websites which are SSL encrypted must retrieve content through an SSL request.
    1. This is due an error message which only occurs on IE. The problem has been reported multiple times (387991,388636), but the people who report it don’t suggest removing the warning. (Firefox, Safari, Opera, and Chrome do not have an error/warning message).
    2. When the IE feedback stops giving me an error message after I sign in, I will add a “feedback” post which requests that the feature be dropped. The link to that “feedback” post will be attached here, so others may second the feature drop. (IE Team: please stop producing this error/warning message; its so unnecessary).

** alternative suggestion: Add version subdirectories for our applications. It can simplify things for the program. But it does complicate maintenance for the developer. Not much though. Please, ask for this if you think it will be less maintenance.

Remember: This can always be updated. And as things change this should be updated.

Technorati Tags: ,,

SiteMapProvider Exception Crashes the Website

on Saturday, April 25, 2009

Problem

I’m getting this error message from Visual Studio (The debugger cannot continue running the process. Process was terminated.):

image

The error message is appearing when debugging a ASP.NET MVC application in Visual Studio 2008. The lines of code which are being executed at taking a object tree and converting it to populate a SiteMapProvider. The error is occuring when stepping from line 10 to line 16. Line 16 is highlighted when error occurs:

Code Snippet 1 (CS1)

   1: private static InsideSaSiteMapNode CreateMvcNode( InsideSaSiteMapNodeDTO dto )
   2: {
   3:     var node                = new InsideSaSiteMapIntegrationNode( _Provider, dto.Href );
   4:     node.Href               = dto.Href;
   5:     node.Title              = dto.Title;
   6:     node.JavascriptAction   = dto.JavascriptAction;
   7:     node.IconFilename       = dto.IconFilename;
   8:     node.Target             = dto.Target;
   9:  
  10:     foreach( var child in dto.ChildNodes )
  11:     {
  12:         var childNode    = CreateMvcNode( child );
  13:         node.ChildNodes.Add( childNode );
  14:     }
  15:  
  16:     return node;
  17: }

As a bonus, the “Process” being referenced in the error message is the ASP.NET Development Server which is running the website. So, the website crashes.

Analysis

The error first started when I added a line of to the SiteMapProvider method, GetChildNodes. The new code is on line 11 (commented out).

CS2

   1: /// <summary>
   2: /// When overridden in a derived class, retrieves the child nodes of a specific <see cref="T:System.Web.SiteMapNode"/>.
   3: /// </summary>
   4: /// <returns>
   5: /// A read-only <see cref="T:System.Web.SiteMapNodeCollection"/> that contains the immediate child nodes of the specified <see cref="T:System.Web.SiteMapNode"/>; otherwise, null or an empty collection, if no child nodes exist.
   6: /// </returns>
   7: /// <param name="node">The <see cref="T:System.Web.SiteMapNode"/> for which to retrieve all child nodes. 
   8: /// </param>
   9: public override SiteMapNodeCollection GetChildNodes( SiteMapNode node )
  10: {
  11:     //return node.ChildNodes;
  12:     return null;
  13: }

I realize that your not supposed to use the incoming node to return the child values. But, that line of code has not been called yet in the program. The error occurs in line CS1:16. The value being returned was made by a call from CS1:12; which might mean that CS1:13 is “pre-executed” when the return value is known. The frustration really lies in the idea that node.ChildNodes (which returns a null value) causes an exception which crashes the web server while returning null produces an exception which is handled in the debugger.

Solution

As you can see in the code above, the offending line of code was commented out. But, it was a good lesson to learn about SiteMapProvider development. When calling the Add method on a SiteMapNode the provider’s abstract methods will get called; and they’re a bit touchy.


Technorati Tags: ,,,

Assembly NullReferenceException & .NET Reflector

on Monday, April 20, 2009

Problem

I was receiving a NullReferenceException when running a third party dll through NUnit. Which made it difficult to debug, because I couldn’t step through/alter the third party dll’s source code.

Analysis

Thankfully the stack trace showed the class, method, and line number of the error. I was able to look through the third party dll using Reflector, and found this line:

builder.AppendLine(Assembly.GetEntryAssembly().Location);

It seemed pretty straight forward that the GetEntryAssembly() method call was returning a null value. But, why?

The explanation came from the remarks section (among other places on the page) of the MSDN documentation:

The GetEntryAssembly method can return null (Nothing in Visual Basic) when a managed assembly has been loaded from an unmanaged application. For example, if an unmanaged application creates an instance of a COM component written in C#, a call to the GetEntryAssembly method from the C# component returns null, because the entry point for the process was unmanaged code rather than a managed assembly.

I had always thought that NUnit was managed code, but maybe there was an intermediate level of unmanaged code which was executing NUnit. This seemed probable because NUnit was being called from NAnt which was being called from CruiseControl.Net. And, the error was not happening when I was ran NUnit through ReSharper within Visual Studio.

I just needed a way to update the third party source code to remove/rewrite the offending line.

Solution

Reflector has a FileDisassembler addin which can take the decompiled output and write it to disk. It can even take an entire dll and create a Visual Studio project when exporting the code.

The decompiled output for this assembly had a few compile errors, but started working after a few redundant “compile-fix-error” steps in Visual Studio.

Once the solution was updated to use the decompiled code, I added in a static method which replaced all calls to Assembly.GetEntryAssembly() within the project:

using System.Reflection;
 
namespace Some.Namespace
{
    /// <summary>
    /// This class handles retrieving assemblies which have the potential of returning null values.
    /// </summary>
    public static class AssemblyHandler
    {
 
        /// <summary>
        /// Retrieves the entry assembly when available. If it is not available then the calling
        /// assembly is returned.
        /// </summary>
        /// <returns>The highest level executing assembly which can be found within executing AppDomain.</returns>
        public static Assembly GetEntryAssembly() {
            Assembly    asm    = Assembly.GetEntryAssembly();
            if( asm == null )    asm    = Assembly.GetCallingAssembly();
            return asm;
        }
 
    }
}


After a new compile, the NullReferenceException disappeared.




Creative Commons License
This site uses Alex Gorbatchev's SyntaxHighlighter, and hosted by herdingcode.com's Jon Galloway.