Simon Green's Developer Blog
Developing .NET in the cold white north ...

Minor Update to NHibernate Helper Project

Thursday, 21 July 2005 16:28 by Simon

Someone pointed out a mistake with the NHibernate Helper Project which has now been updated:

You should never rethrow an exception you've caught as you lose stack trace information.  Rather than:

try
{
   thing.Save();
   transaction.Commit();
}
catch (Exception ex)
{
   transaction.Rollback();
   throw ex;
}

do:

try
{
   thing.Save();
   transaction.Commit();
}
catch (Exception)
{
   transaction.Rollback();
   throw;   // doesn't lose stacktrace.
}

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:  
Categories:   .NET
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

Installing .NET 2.0 Assembly into GAC on .NET 1.1 Only machine

Wednesday, 20 July 2005 16:27 by Simon

Here's an issue I ran into today in case anyone else has the same problem.

Having updated our components to include versions compiled against .NET 2.0 (beta 2) I added them to the MSI installer using InstallAnywhere.Net. The installer copies the assemblies to the file system and also installs them into the GAC. It was a simple update and ran fine on the test machines I had (Windows XP, 2000 and 2003).

However, I've since had a couple of support calls from people getting an error when the installer is trying to copy some of the files. After a lot of work to reproduce the problem it appears that the .NET 2.0 version assembly causes problems when it is copied to the GAC *if* running on Windows 2003 pre SP-1.

The easiest solution is to add some options to our installer so that the .NET 2.0 version files are not installed by default and are an optional component. It may be useful to also provide the option to install components into the GAC or not fo those who want to have access to the .NET 2.0 versions but who don't have .NET 2.0 (beta 2) installed.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:  
Categories:   .NET
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

HTTP Compression - .NET vs IIS/ISAPI

Thursday, 14 July 2005 15:57 by Simon

Many major sites such as Google, Amazon and eBay all have one thing in common - they use HTTP Compression extensively both to speed up page download times and save on bandwidth.
 
If you are using IIS and want to benefit from web compression then you have a number of options available. You can use the IIS built-in compression filter (available in IIS 5.0 and improved in IIS 6.0) or any one of several other commercial ISAPI filters:
 
http://www.pipeboost.com/
http://www.httpzip.com/
http://www.xcache.com/
 
If you are using ASP.NET then you can of course use our ASPAccelerator.NET component or HttpCompress.
 
As you can guess, they are all pretty similar in that fundamentally they do the same thing (reduce page output size using HTTP compression) but there are significant differences depending on whether you go for a .NET approach or an ISAPI one. So which one should you go for and why?
 
ISAPI filters work at the IIS level which means that they can act on any request made to the web server. An ISAPI compression filter can typically compress static site content such as HTML files as well as dynamic application output. A .NET module on the other hand can only process requests that are handled by the ASP.NET engine.
 
Installing an ISAPI filter requires administrator permissions and console access to the web-server. Enabling HTTP Compression in IIS is normally something that the server administrator would do and requires changes to the IIS MetaBase. Adding a .NET module is much easier and can still be done by the administrator at the server-level by changing the machine.config file but also by the application developer / web author by including the assembly and changing the web.config file.
 
This is perhaps the biggest difference between the two - HTTP Compression deployment and configuration is part of your application so is usable on shared hosting without any admin/console access and configuration can be made to suit your app (eg. different compression levels may be appropriate for different pages).
 
This makes the .NET solutions much more appropriate for application developers who want to build HTTP Compression into their application whereas the IIS/ISAPI approach is targeted more towards server administrators.
 
In terms of performance there are a lot of factors involved. Our own .NET solution was significantly faster than the IIS 5.0 ISAPI filter but since Microsoft put a lot of work into optimising the compression filter for IIS 6.0 the difference is now negligable. For many situations the .NET solution can still be faster though.
 
Because ASPAccelerator.NET is a .NET Module and runs as part of your ASP.NET application it is aware of ASP.NET Output Caching and so can avoid re-compressing already compressed pages which can make a big difference to CPU usage on a busy server. IIS doesn't know whether the page output has just been generated or is the same cached version that has been served out 1,000 times before - it re-compresses the dynamic output again each time.
 
If you are interested in HTTP Compression then you are likely to be interested in performance and are probably already using or looking at ASP.NET Output Caching. If so, I'd recommend comparing the compression performance of .NET and ISAPI solutions with Output Caching is used.
One other thing worth mentioning is configuration. Whether a particular request can be compressed or not depends on features of the request. While IIS allows some configuration the level of control available is not as powerful as the rules-engine that we use for ASPAccelerator.NET which allows complete control over the content negotiation process.

Finally, while the ISAPI solutions can compress output of web-services (if configured correctly) the client cannot by default uncompress these. We provide additional helper classes that enables HTTP Compression support to web-service clients. This works at the protocol level so the same classes can be used whatever compression solution is being used on the server.

In addition, we also include WSE 2.0 filters to add compression to SOAP packets. This enables compression of SOAP requests going both from and to the server and would, for instance, allow DIME attachments being uploaded to the server to be compressed (the WSE filter needs to be added to the client and the server and is a proprietary extension).

Whatever the traffic on your site, or the solution that suits you best, I'd recommend you giving HTTP Compression a try - it provides real ROI (if your bandwidth bill is large enough or you are on the limit with your hosting allowance) and avoids having slow downloads that some users may not be willing to wait for.

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:   , , ,
Categories:   .NET
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

NHibernate.Helper Project

Thursday, 7 July 2005 16:16 by Simon

When using NHibernate in an ASP.NET application it is important to manage sessions correctly. Typically, a session-per-request pattern is used with a single session created and used for the lifetime of a request and then discarded. This helper project makes using NHibernate with ASP.NET easier by automating the creation of sessions and ensuring that they are closed after each request. It also provides some convenient methods to make coding simpler and more readable:

Automatically create the database schema:

Db.CreateDatabase();
Load an object from the database

Customer customer = (Customer)Db.Load(typeof(Customer), 123);
Save an object:

Db.Save(Customer);
Delete an object:

Db.Delete(Customer);
Access to the NHibernate Configuration and SessionFactory objects are provided if needed.

Configuration
NHibernate already has a comprehensive and flexible configuration system where settings can be defined in code, in the web.config file or in a separate XML file. Using the separate XML file enables the location of the mapping files to be configured as required. The configuration below defines the NHibernate connection properties and driver classes to use (these could also be defined in the web.config file) and the location of the nhibernate mapping files (in this case the assembly 'MyAssembly' which would contain embedded resources).

using System;
using System.Web;
namespace NHibernate.Helper
{
   /// <summary>    
   /// HttpModule to automatically close a session at the end of a request    
   /// </summary>    
   public sealed class Module : IHttpModule
   {
      public void Init(HttpApplication context)
      {
         context.EndRequest += new EventHandler(EndRequest);
      }
      public void Dispose() { }
      public void EndRequest(Object sender, EventArgs e)
      {
         Db.CloseSession();
      }
   }
}

The Db class contains all the helper methods and manages the creation and lifetime of the sessions (with the HttpModule above).

using System;
using System.Web;
using System.Configuration;
using NHibernate;
using NHibernate.Cfg;
namespace NHibernate.Helper
{    
   /// <summary>    
   /// Provides access to a single NHibernate session on a per request basis.    
   /// </summary>    
   /// <remarks>    
   ///        NHibernate requires mapping files to be loaded. These can be stored as    
   ///        embedded resources in the assemblies alongside the classes that they are for or    
   ///        in separate XML files.    
   ///        <br /><br />    
   ///        As the number of assemblies containing mapping resources may change and    
   ///        these have to be referenced by the NHibernate config before the SessionFactory    
   ///        is created, the configuration is stored in a separate config file to allow    
   ///        new references to be added. This would also enable the use of external mapping    
   ///        files if required.    
   ///        <br /><br />    
   ///        The name of the NHibernate configuration file is set in the appSettings section    
   ///        with the name "nhibernate.config". If not specified the the default value of    
   ///        ~/nhibernate.config is used.    
   ///        <br /><br />    
   ///        Note that the default config name is not used because the .xml extension is    
   ///        public and could be downloaded from a website whereas access to .config files is    
   ///        automatically restricted by ASP.NET.   
   /// </remarks>    
   public sealed class Db
   {        
      /// <summary>        
      /// Key used to identify NHibernate session in context items collection        
      /// </summary>       
      private const string sessionKey = "NHibernate.Db";        
     
      /// <summary>        
      /// NHibernate Configuration        
      /// </summary>       
      private static readonly Configuration configuration = new Configuration();       
     
      /// <summary>        
      /// NHibernate SessionFactory        
      /// </summary>        
      private static readonly ISessionFactory sessionFactory = configuration.Configure( HttpContext.Current.Request.MapPath(ConfigurationSettings.AppSettings["nhibernate.config"]) ).BuildSessionFactory();   
     
      /// <summary>      
      /// NHibernate Session. This is only used for NUnit testing (ie. when HttpContext    
      /// is not available.      
      /// </summary>
      private static readonly ISession session = sessionFactory.OpenSession();       
      /// <summary>    
      /// None public constructor. Prevent direct creation of this object.         
      /// </summary>        
      private Db() { }        
      /// <summary>        
      /// See beforefieldinit        
      /// </summary>        
      static Db() { }        
      /// <summary>        
      /// Creates a new NHibernate Session object if one does not already exist.        
      /// When running in the context of a web application the session object is        
      /// stored in HttpContext items and has 'per request' lifetime. For client apps        
      /// and testing with NUnit a normal singleton is used.        
      /// </summary>        
      /// <returns>NHibernate Session object.</returns>        
      [CLSCompliant(false)]        
      public static ISession Session        
      {
         get             
         {                 
            ISession session;                
            if (HttpContext.Current == null)                
            {                    
               session = Db.session;                
            }                
            else                
            {                    
               if (HttpContext.Current.Items.Contains(sessionKey))                
               {                    
                  session = (ISession)HttpContext.Current.Items[sessionKey];                 
               }             
               else            
               {                
                  session = Db.sessionFactory.OpenSession();    
                  HttpContext.Current.Items[sessionKey] = session;    
               }        
            }             
            return session;        
         }                        
      }
     
      /// <summary>        
      /// Closes any open NHibernate session if one has been used in this request.<br />        
      /// This is called from the EndRequest event.        
      /// </summary>        
      [CLSCompliant(false)]       
      public static void CloseSession()    
      {        
         if (HttpContext.Current == null)    
         {              
            Db.session.Close();  
         }     
         else          
         {               
            if (HttpContext.Current.Items.Contains(sessionKey))    
            {            
               ISession session = (ISession)HttpContext.Current.Items[sessionKey];  
               session.Close();    
               HttpContext.Current.Items.Remove(sessionKey);  
            }    
         }           
      }        
     
      /// <summary>        
      /// Returns the NHibernate Configuration object.        
      /// </summary>        
      /// <returns>NHibernate Configuration object.</returns>        
      [CLSCompliant(false)]     
      public static Configuration Configuration  
      {          
         get { return Db.configuration; }  
      }   
     
      /// <summary>        
      /// Returns the NHibernate SessionFactory object.        
      /// </summary>        
      /// <returns>NHibernate SessionFactory object.</returns>        
      [CLSCompliant(false)]        
      public static ISessionFactory SessionFactory      
      {          
         get { return Db.sessionFactory; }      
      }       
     
      /// <summary>        
      /// Loads the specified object.        
      /// </summary>        
      /// <param name="type">Type.</param>        
      /// <param name="id">Id.</param>
      public static void Load(System.Type type, object id)
      {          
         Db.Session.Load(type, id);  
      }        
     
      /// <summary>        
      /// Gets the specified object.        
      /// </summary>        
      /// <param name="type">Type.</param>        
      /// <param name="id">Id.</param>        
      public static object Get(System.Type type, object id)    
      {           
         return Db.Session.Get(type, id); 
      }        
     
      /// <summary>        
      /// Save object item using NHibernate.        
      /// </summary>        
      /// <param name="item">Object to save</param>        
      public static void Save(object item)     
      {           
         ITransaction transaction = Db.Session.BeginTransaction();   
         try        
         {              
            Db.Session.Save(item);   
            transaction.Commit();  
         }         
         catch (Exception ex)  
         {               
            transaction.Rollback(); 
            throw;         
         }   
      }        
     
      /// <summary>        
      /// Save object item using NHibernate.        
      /// </summary>        
      /// <param name="item">Object to delete</param>        
      public static void Delete(object item)      
      {         
         ITransaction transaction = Db.Session.BeginTransaction(); 
         try       
         {          
            Db.Session.Delete(item);   
            transaction.Commit();       
         }           
         catch (Exception ex)   
         {               
            transaction.Rollback();  
            throw;          
         }     
      }        
     
      /// <summary>        
      /// Creates the specified database.        
      /// </summary>        
      /// <param name="script">Script.</param>        
      /// <param name="export">Export.</param>        
      public static void CreateDatabase()     
      {           
         NHibernate.Tool.hbm2ddl.SchemaExport se = new NHibernate.Tool.hbm2ddl.SchemaExport(Db.Configuration);
         se.Create(true, true); 
      } 
   }
}

 

Currently rated 4.3 by 3 people

  • Currently 4.333333/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:   ,
Categories:   .NET
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed

VistaDB Driver and Dialect for NHibernate

Sunday, 3 July 2005 16:12 by Simon

I've just started work on a VistaDB driver and dialect for NHibernate.

NHibernate is an excellent OR/M (Object Relational Mapping) Object Persistence framework for .NET and VistaDB is an embedded database designed to server as a replacement to Microsoft JET/ MSDE and Borland BDE.

Putting the two together should enable easy xcopy deployment of applications with practically no configuration, something especially useful for people evaluating an application, while still allowing the backend database to be replaced with something more heavy-duty if required and available (such as SQL Server or Oracle).

I have the main functionality working (saving and loading objects) although due to the way that identity columns work in VistaDB I have needed to change a line in NHibernate to support it (hopefully will be accepted as it does not effect any other drivers).

The next step is to ensure all the data-types are mapped to the correct (and supported) types in VistaDB and also to make sure the NHibernate NUnit tests run ok.

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags:   , ,
Categories:   .NET
Actions:   E-mail | del.icio.us | Permalink | Comments (0) | Comment RSSRSS comment feed