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

CodeSmith templates for NHibernate Class / Mapping generation

Sunday, 4 December 2005 16:43 by Simon

Scott Beeler of AMD kindly sent some updates that he made to the original templates which include the following changes:

  • Fixed support for Many-to-Many relationships. These weren't handled correctly.
  • Improved naming of properties.
  • Added checking for lengths of strings to property set methods.
  • Automatic built-in support of the IComparable interface. It supports by-property, bidirectional sorting. This makes using an Nhibernate generated arraylist of objects easy to use as the datasource of something like a gridview where you want to enable bidirectional sorting of any column

Many thanks Scott !

This also addresses an enquiry someone raised about the use of cascade="all" in many-to-many mappings here: http://frazzleddad.blogspot.com/2005/12/nhibernate-many-to-many-confusion.html

The templates are available for download from http://www.intesoft.net/nhibernate/

 

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

Build versioning with NAnt, Draco.NET and SourceGear Vault

Friday, 21 October 2005 16:33 by Simon

One aim of continuous integration should be to automate as much of the build process as possible. If something is left to human intervention then inevitably it will at some point be completed incorrectly or inconsistently or forgotten completely.

Part of a good build process is to update the version number of the application so that each build (triggered by changes in the source) will have a unique number and the source control system should label the source so that the code that created that build can be identified. This is my solution to updating the version number when using NAnt, Draco.Net and SourceGear Vault.

The build process consists of the following:

Draco.Net monitors the project folder in the SourceGear Vault repository.
A developer checks in some changes which causes Vault to update the folder version number (based on the changeset).
Draco.Net detects the changes and gets the latest version of the project to a temporary build folder
The NAnt .build file (part of the project) is started by Draco.Net
Within NAnt a custom function gets the folder version from Vault and sets the revision part of the version number based on it
The project folder in Vault is labeled with the build revision number
The assembly is compiled using the version number by overriding the AssemblyInfo.cs file (using the standard <adminfo> task)
The NUnit tests are run and reports produced, MSI files created etc.
Everything is pretty straightforward using the standard functions of NAnt, Draco.Net and the SourceGear Vault NAnt Tasks other than step #5 which required a custom NAnt function to return the folder tree version number. The code for this is below which I added to the Vault NAnt Tasks project (I'm not sure yet of the process for feeding this into the project as an update):

<?xml version="1.0"?>
<project name="NUnitAspInjector" default="build" basedir=".">
 <!-- set by Draco.Net for automated builds -->
 <property name="draco" value="false"/>
 <property name="build.id" value="0"/>
 <!-- project properties -->
 <property name="project.name" value="NUnitAspInjector"/>
 <!-- base project version for this trunk / branch -->
 <property name="build.version.major" value="1"/>
 <property name="build.version.minor" value="0"/>
 <property name="build.version.build" value="5000"/>
 <property name="build.version.revision" value="0"/>
 <!-- if automated build then set revision number based on version in vault -->
 <if test="${draco}">
  <!-- vault parameters (set in Draco.Net project properties) -->
  <!--  <property name="vault.url"  value="http://localhost"/>  <property name="vault.repository" value="default"/>  <property name="vault.path"  value="$/NUnitAspInjector/trunk"/>  <property name="vault.username"  value="build"/>  <property name="vault.password"  value="password"/>   -->
  <!-- set revision number based on tree version from vault -->
  <property name="build.version.revision" value="${vault::getversion(vault.url, vault.repository, vault.path, vault.username, vault.password)}"/>
  <!-- label the build -->
  <!-- in case a build is forced, this avoids a duplicate label error -->
  <vaultdeletelabel    url="${vault.url}"    username="${vault.username}"    password="${vault.password}"    repository="${vault.repository}"    path="${vault.path}"    labelstring="build ${build.version.revision}"/>
  <!-- apply the label to the source folder -->
  <vaultlabel    url="${vault.url}"    username="${vault.username}"    password="${vault.password}"    repository="${vault.repository}"    path="${vault.path}"    labelstring="build ${build.version.revision}"/>
 </if>
 <!-- set the full build version based on major.minor.build.revision -->
 <property name="build.version" value="${build.version.major}.${build.version.minor}.${build.version.build}.${build.version.revision}"/>
 <!-- output directory -->
 <property name="build.outdir" value="C:\working\buildoutput\${project.name}\${build.version}"/>
 <!-- target .NET framework and corresponding compiler directives -->
 <property name="nant.settings.currentframework" value="net-1.1"/>
 <property name="csc.define" value="NET11"/>
 <target name="build">
  <echo message="building: ${project.name} ${build.version}"/>
  <echo message="build id: ${build.id}" if="${draco}"/>
  <!-- build the solution in debugh mode so that the test are created -->
  <!-- this is a shortcut until a proper test project is created -->
  <solution solutionfile="src\NUnitAspInjector.sln" configuration="debug">
   <webmap>
    <map url="http://localhost/NUnitAspInjector.Sample/" path="src\NUnitAspInjector.Sample\"/>
   </webmap>
  </solution>
  <!-- make a folder for the assembly -->
  <mkdir dir="bin"/>
  <!-- if automated build then create assembly version info -->
  <if test="${draco}">
   <attrib file="src\NUnitAspInjector\AssemblyInfo.cs" readonly="false"/>
   <asminfo output="src\NUnitAspInjector\AssemblyInfo.cs" language="CSharp">
    <imports>
     <import name="System" />
     <import name="System.Reflection" />
     <import name="System.EnterpriseServices" />
     <import name="System.Runtime.InteropServices" />
    </imports>
    <attributes>
     <attribute type="ApplicationNameAttribute" value="${project.name}" />
     <attribute type="AssemblyProductAttribute" value="${project.name}" />
     <attribute type="AssemblyTitleAttribute" value="InteSoft ${project.name} ${build.version.major}.${build.version.minor} for .NET 1.1" />
     <attribute type="AssemblyDescriptionAttribute" value="${project.name} for ASP.NET" />
     <attribute type="AssemblyCompanyAttribute" value="InteSoft IT Ltd." />
     <attribute type="AssemblyCopyrightAttribute" value="Copyright &#x00A9; 2005 InteSoft IT Ltd." />
     <!-- the version stays at x.x.x.0 for binding -->
     <attribute type="AssemblyVersionAttribute" value="${build.version.major}.${build.version.minor}.${build.version.build}.0" />
     <!-- the information versions show the revision number -->
     <attribute type="AssemblyFileVersionAttribute" value="${build.version}" />
     <attribute type="AssemblyInformationalVersionAttribute" value="${build.version}" />
     <attribute type="AssemblyDelaySignAttribute" value="false" />
     <attribute type="AssemblyKeyFileAttribute" value="" />
     <attribute type="AssemblyKeyNameAttribute" value="" />
     <attribute type="CLSCompliantAttribute" value="true" />
     <attribute type="ComVisibleAttribute" value="false" />
    </attributes>
    <references>
     <include name="System.EnterpriseServices.dll" />
    </references>
   </asminfo>
  </if>
  <!-- now build the proper release version of our component -->
  <csc target="library" output="bin\NUnitAspInjector.dll" debug="false" define="${csc.define};" optimize="true" rebuild="true">
   <sources basedir="src\NUnitAspInjector">
    <include name="AssemblyInfo.cs"/>
    <include name="Gdi32.cs"/>
    <include name="Handler.cs"/>
    <include name="Html2Image.cs"/>
    <include name="InjectorModule.cs"/>
    <include name="InjectorWebFormTestCase.cs"/>
    <include name="RenderAssertion.cs"/>
    <include name="TestHostAttribute.cs"/>
    <include name="TestInjectorAttribute.cs"/>
    <include name="TestRenderAttribute.cs"/>
    <include name="TestServer.cs"/>
    <include name="cassini\ByteParser.cs"/>
    <include name="cassini\ByteString.cs"/>
    <include name="cassini\Connection.cs"/>
    <include name="cassini\Host.cs"/>
    <include name="cassini\Messages.cs"/>
    <include name="cassini\Request.cs"/>
    <include name="cassini\Server.cs"/>
   </sources>
   <resources prefix="NUnit.Extensions.Asp" basedir="src\NUnitAspInjector">
    <include name="Html2Image.resx"/>
   </resources>
   <references>
    <include asis="true" name="System.Web.dll"/>
    <include asis="true" name="System.Web.Services.dll"/>
    <include asis="true" name="System.Runtime.Remoting.dll"/>
    <include asis="true" name="System.XML.dll"/>
    <include asis="true" name="System.Windows.Forms.dll"/>
    <include asis="true" name="System.Drawing.dll"/>
    <include asis="true" name="lib\AForge.Imaging.dll"/>
    <include asis="true" name="lib\AForge.Math.dll"/>
    <include asis="true" name="lib\nmock.dll"/>
    <include asis="true" name="lib\nunit.framework.dll"/>
    <include asis="true" name="lib\NUnitAsp.dll"/>
    <include asis="true" name="lib\Interop.SHDocVw.dll"/>
    <include asis="true" name="lib\AxInterop.SHDocVw.dll"/>
    <include asis="true" name="lib\Microsoft.mshtml.dll"/>
   </references>
  </csc>
  <!-- copy this release version to the test app -->
  <copy file="bin\NUnitAspInjector.dll" todir="src\NUnitAspInjector.Sample\bin" overwrite="true"/>
  <!-- run the NUnit tests -->
  <nunit2 failonerror="true">
   <formatter type="Xml" usefile="true" extension=".xml" outputdir="${build.outdir}"/>
   <test assemblyname="src\NUnitAspInjector.Sample\bin\NUnitAspInjector.Sample.dll" appconfig="src\NUnitAspInjector.Sample\web.config">
    <categories>
     <exclude name="IIS"/>
    </categories>
   </test>
  </nunit2>
  <!-- create NUnit report -->
  <nunit2report out="NUnitReport.htm" todir="${build.outdir}" opendesc="true">
   <fileset>
    <include name="${build.outdir}\NUnitAspInjector.Sample.dll-results.xml"/>
   </fileset>
  </nunit2report>
  <!-- copy this release version to the output folder -->
  <copy file="bin\NUnitAspInjector.dll" todir="${build.outdir}" overwrite="true"/>
 </target>
</project>

The key line is this:

<!-- set the revision number based on a build startdate -->
<property name="build.startdate" value="01 Oct 2005"/>
<script language="C#">
 <imports>
  <import namespace="System.Globalization"/>
  <import namespace="System.Threading"/>
 </imports>
 <code>
 <![CDATA[
  public static void ScriptMain(Project project)
  {
   DateTime start = Convert.ToDateTime(project.Properties["build.startdate"]);   
   Calendar calendar = Thread.CurrentThread.CurrentCulture.Calendar;   
   int months = ((calendar.GetYear(DateTime.Today) - calendar.GetYear(start)) * 12) + calendar.GetMonth(DateTime.Today) - calendar.GetMonth(start);   
   int day = DateTime.Now.Day;   
   int revision = (months * 100) + day;   
   project.Properties["build.version.revision"] = revision.ToString();  
  }  
 ]]>
 </code>
</script>

However, I like the fact that the build number only changes when the source changes (due to checkins) rather than incrementing simply due to time. Also, with Vault, the version number of the folder ends up matching the build label applied to it which just seems neat. I like neat :)

That's it. Hope someone finds this of some use!

 

Be the first to rate this post

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

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

Windows 2003 SP1 .NET Encoding Issue

Tuesday, 11 October 2005 16:31 by Simon

We recently came across an issue with one of our components (ASPRedirector.NET) where a customer who had installed Windows 2003 SP1 started getting errors on their website. The errors only happened if they used a certain encoding in the globalisation settings:

public class Filter : Stream
 {
  private Encoder encoder;
  private Decoder decoder;
  private Stream baseStream;

  public Filter(HttpContext context)
  {
   Encoding encoding = context.Response.ContentEncoding;
   this.encoder = encoding.GetEncoder();
   this.decoder = encoding.GetDecoder();
   this.baseStream = context.Response.Filter;
  }

  public override bool CanRead
  {
   get { return false; }
  }

  public override bool CanWrite
  {
   get { return true; }
  }

  public override bool CanSeek
  {
   get { return false; }
  }

  public override void Close()
  {
   baseStream.Close();
  }

  public override void Flush()
  {
   baseStream.Flush();
  }

  public override long Length
  {
   get { throw new NotSupportedException(); }
  }

  public override long Seek(long offset, SeekOrigin origin)
  {
   throw new NotSupportedException();
  }

  public override void SetLength(long value)
  {
   throw new NotSupportedException();
  }

  public override long Position
  {
   get { throw new NotSupportedException(); }
   set { throw new NotSupportedException(); }
  }

  public override int Read(byte[] buffer, int offset, int count)
  {
   throw new NotSupportedException();
  }

  public override void Write(byte[] buffer, int offset, int count)
  {
   // get number of characters that the byte array will be decoded into
   int charCount = this.decoder.GetCharCount(buffer, offset, count);
   // create character array to hold them
   char[] chars = new char[charCount];
   // decode byte array into characters
   int charCountDecoded = decoder.GetChars(buffer, offset, count, chars, 0);

   // we now have characters and can process them
   // converting to a string should be the readable content
   string content = new string(chars);

   // get number of bytes that the character array will be encoded into
   int byteCount = encoder.GetByteCount(chars, 0, charCountDecoded, true);
   // create byte array to hold them
   byte[] bytes = new byte[byteCount];
   // encode character array into bytes
   int byteCountEncoded = encoder.GetBytes(chars, 0, charCountDecoded, bytes, 0, true);

   // write bytes to output stream - this should be exactly what came in
   baseStream.Write(bytes, 0, byteCountEncoded);
  }
 }

This fails if the responseEncoding is set to "iso-8859-15" after Windows 2003 SP1 has been applied but works before SP1 and if a different encoding is used (the default "utf-8" for instance).

So what's the problem? I found a lone post in a newsgroup that explained it:

SP1 of Windows Server introduced a new "feature" in the GlobalizationConfig class called EnableBestFitResponseEncoding.

Effect is that when you set anything other than UTF8 in web.config, e.g. <globalization requestEncoding="iso-8859-1" responseEncoding="iso-8859-1" />, many functions will break (including HttpUtility.URLDecode) with a MethodNotImplementedExecption in the new internal class CodePageNoBestFitEncoding.

Analyzing with the reflector reveals, that new code was added to the HttpResponse.ContentEncoding property:

                  if (!this._encoding.Equals(Encoding.UTF8))
                  {
                        string text1 = this._encoding.GetType().FullName;
                        if ((config1 == null) || !config1.EnableBestFitResponseEncoding)
                        {
                              if (text1 == "System.Text.MLangCodePageEncoding")
                              {
                                    this._encoding = Encoding.GetEncoding("mlang");
                              }
                              else if ((text1 == "System.Text.CodePageEncoding") || (text1 == "System.Text.Latin1Encoding"))
                              {
                                    int num1 = this._encoding.CodePage;
                                    this._encoding = new CodePageNoBestFitEncoding(num1);
                              }
                        }

Assuming that EnableBestFitResponseEncoding is false by default, this property will return a new instance of CodePageNoBestFitEncoding when you set a responseEncoding="iso-8859-1" in web.config.

Many of the normal Encoding methods in CodePageNoBestFitEncoding will throw the MethodNotImplemented exception which explains the above mentioned bahavior.

Some additional analysis reveals that there is a new attribute for <globalization > called "enableBestFitResponseEncoding". After setting this attribute to "true", everthing works again as normal with a responseEncoding other than UTF8.

A search in Microsoft's site for "enableBestFitResponseEncoding" returns 0 hits. Thank you Microsoft!

 

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

NUnitAspInjector Project

Sunday, 9 October 2005 16:29 by Simon

This is a helper library to make unit testing with NUnit, NUnitAsp and NMock easier. It builds on some of the ideas here and also adds some new features such as the ability to create unit tests that match the rendered output of a page or control against a base-line image. TDD for HTML and CSS !

I still have to do a complete write-up on how to use it but for those that like to download and explore, here it is:

http://www.intesoft.net/NUnitAspInjector/

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

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