Archive

Archive for the ‘.net 2.0’ Category

Using Multiple NHibernate Session Factories

March 6, 2009 1 comment

A few months ago, I rewrote a few of my base repositories to follow some of the archtecture practices of the S#arp Architecture project.  I liked how easy it was to setup NHibernate*, mark domain objects, and setup mappings and highly recommend checking out their project.  Unfortunately, the whole project was a bit too bulky for what I needed and much of the functionality already existed in existing framework libraries we use at the office.

* When I mention NHibernate, I’m using Fluent NHibernate, currently at build 366 with a bit of modification to allow Oracle data access.  I’ve also enhanced the S#arp Architecture project to take advantage of the latest feature changes in Fluent NHibernate.

One pitfall, however, that I recently ran into was needing to access NHibernate multiple session factories at the same time—such as to access PeopleSoft and our internal student system to pull in demographic information.  The S#arp web session storage, by default, uses a single key opening a separate session overwrites the prior.

I found an open Issue on the S#arp tracker (#12) for such a functionality and a bit of code from Russell Thatcher to allow multiple sessions; however, I could never get it fully working—so I set off to create my own.

Updating the WebSessionStorage

To address the issue of storing multiple sessions in the HttpContext’s session state, the keys must be uniquely named when created.  To do this, I replaced the previous constructor of WebSessionStorage(HttpApplication) and added an additional parameter to create WebSessionStorage(HttpApplication, string).

This appends a name to the session key used to store the NHibernate ISession.

private readonly string CurrentSessionKey = “nhibernate.”;

 

public WebSessionStorage(HttpApplication application, string factoryKey)

{

       application.EndRequest += Application_EndRequest;

       CurrentSessionKey += factoryKey;

}

Everything else in WebSessionStorage remains the same.

NOTE: This ensures that we can find the ISession within the ASP.NET context using WebSessionStorage and isn’t required if you’re managing the the ISession yourself or using another mechanism for storage.

Updating the NHibernateSession

Since we’re looking to support multiple, keyed sessions, the NHibernateSession’s private fields of SessionFactory and SessionStorage should be changed into keyed Dictionaries.

private static readonly Dictionary<string, ISessionFactory> SessionFactories =

       new Dictionary<string, ISessionFactory>();

 

private static readonly Dictionary<string, ISessionStorage> SessionStorages =

       new Dictionary<string, ISessionStorage>();

To use these new dictionaries, we replace the change the logic in the Init method to add the factory and storage to the dictionary, rather than assign to a single variable.

public static FluentConfiguration Init(

       this FluentConfiguration configuration,

       ISessionStorage storage,

       string factoryKey)

{

       Check.Require(storage != null,

              “The session storage was null, but must be provided.”);

       Check.Require(!string.IsNullOrEmpty(factoryKey),

               “A unique key for the session factory was null, but must be provided.”);

 

       try

       {

              if (!SessionFactories.ContainsKey(factoryKey))

             {

                     SessionFactories.Add(

                           factoryKey,

                           configuration.BuildSessionFactory());

                    

                     SessionStorages.Add(

                           factoryKey,

                           storage);

 

                     return configuration;

              }

       }

       catch (HibernateException ex)

       {

              throw new

                     HibernateException(

                           “Cannot create session factory.”, ex);

       }

 

       return configuration;

}

Our Init statement, when called, then looks like, passing along the factory key with the ISessionStorage.

configuration.Init(storage, “FactoryKeyNameGoesHere”);

The Current static public property also needs to address the new Dictionary; however, cannot as a property as it needs the parameter of the factory key.  I converted this into a method that accepts the factory key and returns an ISession based on what it finds.  If it cannot find an open sesson (is null) in the dictionary, it opens a new one, sets it in the dictionary, and returns it to the caller.

public static ISession Current(string factoryKey)

{

       var session =

              SessionStorages[factoryKey].Session ??

              SessionFactories[factoryKey].OpenSession();

 

       SessionStorages[factoryKey].Session = session;

      

       return session;

}

That’s it in the NHibernateSession.

Updating the Repository & Decorating Inheriting Repositories

The Repository base class provides functionality to the CRUD methods through a protected Session property.  This property needs to be updated to call the new Current(string) method.  However, since the Repository doesn’t know, we need to find a way to communicate the “key” that a repository uses to ensure the correct session is accessed (so, for example, that our PeopleSoft queries don’t try to hit our SAP session—that confuses it. *grin*)

Randall had a great solution for this—an attribute applied to repositories that simply provides a place to store the “name” of the session.

[AttributeUsage(AttributeTargets.Class)]

public class SessionFactoryAttribute : Attribute

{

       public readonly string FactoryKey;

 

       public SessionFactoryAttribute(string factoryKey)

       {

              FactoryKey = factoryKey;

       }

 

       public static string GetKeyFrom(object myClass)

       {

              var key = string.Empty;

 

              //Get member info via reflection

              var info = myClass.GetType();

 

              //Retrieve custom attributes

              var attributes =

                     info.GetCustomAttributes(

                           typeof (SessionFactoryAttribute),

                           true);

 

              //Check for attribute

              if (attributes.Length > 0)

              {

                     var att =

                           (SessionFactoryAttribute) attributes[0];

                     key = att.FactoryKey;

              }

             

              return key;

       }

}

Now we simply decorate the inheriting repositories with SessionFactory.  Notice how the key matches what we used in the Init statement earlier.

[SessionFactory(“FactoryKeyNameGoesHere”)]

public class CourseRepository :
       NHibernateRepository<Course>, ICourseRepository, IDisposable { }

For a bit better design, you can also create a constant called, perhaps, FactoryKey or {data}FactoryKey and use that for the attributes and Init statement.  Then, if it needs changed—change it in one place.

Inside our Configuration class (PSConfiguration, for example), we have:

public const string FactoryKey = “PSProd”;

and

configuration.Init(storage, PSConfiguration.FactoryKey);

and then decorating our Repository classes:

[SessionFactory(PSConfiguration.FactoryKey)]

Now, to take advantage of our new SessionFactory attribute, we modify the Session property on our Repository base class.

protected virtual ISession Session

{

       get

       {

              var factoryKey = SessionFactoryAttribute.GetKeyFrom(this);

              return NHibernateSession.Current(factoryKey);

       }

}

Now the interface implementations (Get, Find, etc) are fetching the correct Session.

In summary, I’ve enabled accessing multiple NHibernate Session Factories from the S#arp Architecture framework (or my small implementation of it) by…

  • Updating the WebSessionStorage to accept a factory key when the session is created.
  • Updating the NHibernateSession to store multiple Factories and Storages in dictionaries and then access these dictionaries when the “current” session is requested.
  • Adding an attribute to Repositories to “name” which factory a certain repository should be selecting out of and then providing that session when called.

So far, things are working like a champ—tweaking and refactoring from here. ๐Ÿ™‚

HTML Formatting and Spacing in ActiveReports

March 6, 2009 1 comment

ActiveReport’s RichTextBox is great for presenting HTML-styled content on PDF reports; however, there seems to be a snag with a few of the HTML tags and how the RichTextBox translates the commands to HTML.

To print out a bit of HTML (from a field), I have the following StringBuilder my details_Format:

builder

       .AppendFormat(

       @”{0}On {1}, <strong>{2}</strong> wrote: <br/>{3}<br/><br/>{4}”,

       […]);

However, the <strong> tags are not properly rendering spaces between them:

Issues with Spacing

There seems to be an existing notice on the DataDynamics forums (here) regarding the spacing, but it’s from 2007–-I’d assume it’s been fixed by now; however, I can’t find it in any of the AR3.0 release notes.

So what’s the fix?

Force the spaces in with non-breaking space commands.

       @”{0}On {1},&nbsp;<strong>{2}</strong>&nbsp;wrote: <br/>{3}<br/><br/>{4}”,

Bingo!

Fix With Spacing

Part 2: Converting Generic Collections to CSVs

February 6, 2009 1 comment

Part 1 | Part 2

In my last post, I wrote up the tests needed to meet our customer’s requirements, now I’m ready to flush out the tests.

Objective #1 – Export Collection to CSV With All Properties

Each of my tests implement one of two extension methods, ToDelimitedFile and ToDelimitedRow.  Collections focus on File whereas objects (singles) use the Row. 

Unfortunately, I couldn’t find a 100% accurate way to use the generic constraints—too bad we can’t put in “where T : !ICollection<T>”  Hence the separate methods.

For me, extension methods sit outside of my actual classes in their own happy static {whatever}Extensions class.  The CSV Conversions are no different.

For Test #1 to pass, we need to implement the extension method.

public static string ToDelimitedFile<T>(this ICollection<T> convertible)

{

       return DelimitedExporter.SerializeCollection(convertible);

}

This paves the way for creating our next class and method, DelimitedExporter and SerializeCollection.  The DelimitedExporter class will contain all of the methods and logic for converting our collections into CSV “lines”.  Let’s implement SerializeCollection real quick with some dummy data and rerun the test.

public static string SerializeCollection<T>(ICollection<T> convertible)

{

       return “Name,Level”;

}

Pass!  Test passes!

Now, of course, since the test passes, we’re ready to flush out the details of the SerializeCollection method.

Creating the Header Rows

Since we know we’ll be working with strings and doing a large number of appends, let’s start out by creating a StringBuilder to hold all of that text information.

public static string SerializeCollection<T>(IEnumerable<T> convertible)

{

       var stringBuilder = new StringBuilder();

 

       //grab properties of the method to create columns

       var columns = typeof(T).GetProperties();

 

GetProperties is a method on Type that returns all public properties as PropertyInfo objects.  These properties will be used to create the column header names for our CSV.

       // build header row

       stringBuilder.Append(BuildHeaderRowFrom(columns));

 

So let’s “build” our header row.  I’ve extracted this into a separate method from the start—you could have one mega method and refactor it down later.

 

private static string BuildHeaderRowFrom(IEnumerable<PropertyInfo> columns)

{

       var headerString = string.Empty;

       var counter = 0;

       foreach (PropertyInfo column in columns)

       {

              counter++;

             headerString += column.Name;

             if (counter != columns.Count)

                     headerString += “,”;

       }

       headerString += Environment.NewLine;

 

       return headerString;

}

The BuildHeaderRowFrom method takes the PropertyInfo enumeration from our GetProperties() call and iterates through it, capturing the Name of each property and appending it to a string.

Let’s close up the SerializeCollection() method …

       return stringBuilder.ToString();

}

… and run the test again and everything should pass up to this point.

Now we can focus on the data rows.

Creating the Data Rows

Our next task in SerializeCollection is to flush out how each enumerable object will be converted into a row in our CSV.

After our BuildHeaderRowsFrom() method, let’s now append the data rows with:

       // build item rows

       stringBuilder.Append(BuildDataRowsFrom(collection, columns));

BuildDataRowsFrom will take the original collection of objects (an IEnumerable<T>) and the columns we parsed for the header as parameters.

private static string BuildDataRowsFrom<T>(

       IEnumerable<T> collection,

       IEnumerable<PropertyInfo> columns)

{

       var rowString = string.Empty;

       foreach (T item in collection)

       {

              int counter = 0;

              var columnCount = columns.Count();

             foreach (PropertyInfo column in columns)

             {

                     counter++;

                     var value =

                           column.GetValue(item, null) ?? string.Empty;

 

                     // Append column value to the row.

                     rowString += “{0}”.AsFormatFor(value);

 

                     // Append delimiter if _not_ the last column.

                     if (counter != columnCount)

                           rowString += “,”;

              }

              rowString += Environment.NewLine;

       }

       return rowString;

}

Note: I could use a StringBuilder inside of this; however, the StringBuilder seemed to take a bit longer with the tests of 50,000ish records (max in the system I was designing this for).  A later revision may be to further examine ways to improve performance.

 

So, what’s all this do?

 

For the most part, it reads as a sentence:  For each item in the collection, loop through each known column and fetch the value from that item.  If the item is null, simply return an empty string. 

 

Since I’m using IEnumerable<T> rather than ICollection<T>, I don’t have access to a Count property (without calling the Linq extension method, so I’ve moved that outside of the foreach loop.  Perhaps there’s a better generic collection to be using, but that’s for later revisions.

 

Again, run our test again, and it should pass AND we should now have data.

 

can_convert_list_of_objects_to_csv : Passed

*** ConsoleOutput ***
Name,Level
SchoolA,Elementary
SchoolB,Elementary
SchoolC,Elementary
SchoolD,Elementary

 

Excellent.

 

That’s it for this post.  In the next part, I’ll start in on the second objective—being able to specify which parameters are picked up by the DelimitedExporter.

 

 

Part 1: Converting Generic Collections To CSVs

February 6, 2009 1 comment

I recently had a request to allow “exports” of lists from one application to the user in CSV (comma separated value) format.  After a bit of dinking, here’s what I came up with; however, I’m sure a bit more work will find that it’s overkill. ๐Ÿ™‚

Starting Off With The Tests

So what did I want to accomplish?  I had three objectives:

  1. Export a collection of objects (IList, ICollection, IEnumerable, etc) to CSV with all “properties”.
  2. Export a collection and be able to specify the properties to export.
  3. (optional) Export a single object to CSV with the same two requirements above (1, 2).

#1:

[Fact]

public void can_convert_list_of_objects_to_csv()

{

       var repository = new SchoolRepository();

       var schools = repository.GetBySchoolLevel(Level.Elementary);

 

       var csvOutput = schools.ToDelimitedFile();

 

       // check that the CSV has the headers

       // of the properties of the object

       csvOutput.ShouldContain(“Name,Level”);

}

#2:

[Fact]

public void can_convert_list_of_objects_to_csv_with_parameters()

{

       var repository = new SchoolRepository();

       var schools = repository.GetBySchoolLevel(Level.Elementary);

      

       var csvOutput = schools.ToDelimitedFile(“Name”);

       csvOutput.ShouldContain(“Name”);

 

       Console.Write(csvOutput);

}

#3:

[Fact]

public void can_convert_a_single_object_to_csv()

{

       var repository = new SchoolRepository();

       var school = repository.Get(120);

 

       var csvOutput = school.ToDelimitedRow();

       csvOutput.ShouldContain(“Great School,High”);

 

       Console.Write(csvOutput);

}

In the next post, we’ll begin flushing out our tests.

 

 

Setting the ViewPort for iPhone/iTouch Ready Web Sites

January 13, 2009 Comments off

Scenario:

After rolling out two new web applications targeted for WindowsMobile and Blackberries, we had a request to test and support Apple iPhone/iTouch devices.  Not a big deal, or so I thought.

Unfortunately, Safari on these devices displayed properly, but every page required zooming and was almost impossible to read.  There had to be a way to get Safari to respect the set width of the page rather than scale it out.

Solution:

Michael: “are you setting the viewport?”

Thanks to Google and a bit of help from a friend (thanks Michael!), the mysterious solution was just a meta tag away.  The best place to start would be the “Safari Web Content Guide for iPhone OS”.  I had found bits of this document elsewhere, but the information about the Viewport tags are extremely helpful.

The default viewport width is 980px; however, you can hard code the width of your page.  I placed the following tag in the <head> section of my MasterPage for the mobile site.

<!– meta tag for iPhone/iTouch devices –>

<meta name=”viewport” content=”width=350px” />

That worked; however, the HTML elements (buttons, selects, etc) were still far too small.  It seems that “fit to device” is more explicitly expressed with the device-width value.

<!– meta tag for iPhone/iTouch devices –>

<meta name=”viewport” content=”width=device-width” />

device-width not only fit the text to the screen, but also properly scaled the elements as well.

Excellent. ๐Ÿ™‚

I need to fully read that Safari Web Content Guide to see if there are other wonky tags for iDevice goodness. ๐Ÿ˜‰

PersistanceConfiguration for Oracle/Fluent NHibernate

December 4, 2008 1 comment

On Tuesday, I mentioned that I created a custom PersistanceConfiguration (based on the template for MS SQL) to connect to Oracle.  I had a few individuals email me, so I went ahead and tossed the code out on Google Code for everyone.

Feel free to download the source here.

The class derives FluentNHibernate’s PersistanceConfiguration<T> and uses the Oracle.DataAccess (ODP) driver. 

To use it, run through just like the MS SQL version.  I did, however, add an OtherOptions property to pass caching, pooling, and other Oracle-specific parameters.  I could have put each to a separate property (and may in the future)—as soon as I can get F-NHibernate fully working. Heh.  Priorities, right?

// Add our Oracle configuration to the NHibernate Config.

var oracleConfiguration = OracleConfiguration.Oracle9

.ConnectionString

      .Server(“server”)

      .Port(1521)

      .Instance(“instance.here”)

      .Username(“user”)

      .Password(“p@$$w0rd”)

      .OtherOptions(“Statement Cache Size=50;”)

      .Create;

oracleConfiguration.ConfigureProperties(config);

config.AddMappingsFromAssembly(Assembly.Load(_mappingAssembly));

Fluent NHibernate With Oracle – An Answer…

December 3, 2008 1 comment

Yesterday, I posted up a real annoying issue with getting Fluent NHibernate working with Oracle.  After dinking most of the evening, I found (as with most things) that the issue had little to do with NHibernate and a great deal to do with my lack of understanding.

Ahh, the joys of learning.

So, here’s where I glitched.

1. The ClassMap needs more than to have public constructors—but must be public itself. 

public class StudentMap : ClassMap<Student>

{

       public StudentMap()

       {

              WithTable(“Students”);

 

              Id(x => x.Id, “pupil_number”)

                     .GeneratedBy.Native();

              Map(x => x.FirstName, “first_name”);

              Map(x => x.LastName, “surname”);

 

       }

}

Whoops.

2. The connection string is touchy.  My original connection string looked like:

@“User Id={0};

Password={1};

Pooling=True;

Enlist=False;

Statement Cache Size=50;

Min Pool Size=10;

Incr Pool Size=5;

Decr Pool Size=2;

Data Source= (DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)
(HOST={2})(PORT={3})))(CONNECT_DATA=(SERVICE_NAME={4}))”

Unfortunately, that doesn’t seem to work with the non-ODP driver (aka: the Microsoft driver).  Statement Cache Size and the Pooling Size are unknown parameters.  That’s TOTALLY understandable and is easy to fix.

For now, I can simplify this to:

@“User Id={0};

Password={1};

Pooling=True;

Data Source= (DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)
(HOST={2})(PORT={3})))(CONNECT_DATA=(SERVICE_NAME={4}))”

I’ll work out the ODP drivers as a next step..

That’s it!

With those two changes, my tests pass without any problems AND return the correct information from our database:

Passing Fluent NHibernate Test

Fluent NHibernate With Oracle – A Question…

December 2, 2008 3 comments

Answer found!  Read more here.

After success with standard NHibernate, I quickly became enraptured with the cleaner Fluent NHibernate API.  No more XML; however, I have had VERY limited (read: none) success getting this working against Oracle—especially against our crazy Oracle environment (of which I have no control and readonly access).

Here’s what I have:

A basic ClassMap of a Student object.

class StudentMap : ClassMap<Student>

{

       public StudentMap()

       {

              WithTable(“Students”);

 

              Id(x => x.Id, “pupil_number”)

                     .GeneratedBy.Native();

              Map(x => x.FirstName, “first_name”);

              Map(x => x.LastName, “surname”);

 

       }

}

A basic Student object.

public class Student

{

       private Student() { }

 

       public virtual int Id { get; set; }

       public virtual string FirstName { get; set; }

       public virtual string LastName { get; set; }

}

Note: My constructor is private because this SHOULD be a readonly object.  Setting it public doesn’t seem to matter either. ๐Ÿ˜ฆ

A basic data provider.

public class SISDataProvider

{

       public ISession Session;

 

       public SISDataProvider(ISession session)

       {

              Session = session;

       }

 

       public T GetById<T>(int id)

       {

              Session.Flush();

              return Session.Get<T>(id);

       }

}

No save functionality—again, we just have readonly access to this database.

A basic session manager.

public class SISSessionManager

{

       private readonly string _mappingAssembly;

       private readonly ISessionFactory _sessionFactory;

 

       public SISSessionManager()

       {

              _mappingAssembly = “SIS.Domain”;

              _sessionFactory = GetSessionFactory();

       }

 

       public ISession GetSession()

       {

              return _sessionFactory.OpenSession();

       }

 

       private ISessionFactory GetSessionFactory()

       {

              var config = new Configuration();

 

              var configuration = OracleConfiguration.Oracle9

                .ConnectionString

                    .Server(“server”)

                    .Port(1521)

                    .Instance(“instance.name”)

                    .Username(“user”)

                    .Password(“password”)

                    .Create

                .ConfigureProperties(config);

 

              configuration.AddMappingsFromAssembly(Assembly.Load(_mappingAssembly));

 

              return configuration.BuildSessionFactory();

       }

}

The OracleConfiguration is based on a FluentNhibernate.Cfg.PersistanceConfiguration that I created that calls the OracleClientDriver (not the ODP; that’s OracleDataClientDriver—I’m simply wanting to get the architecture working for now and I’ll dink with increasing performance with ODP as the next step).

Finally, the test.

public class creating_a_student

{

       private SISDataProvider _provider;

       private SISSessionManager _sessionFactory;

 

       public creating_a_student()

       {

              _sessionFactory = new SISSessionManager();

              _provider = new SISDataProvider(_sessionFactory.GetSession());

       }

 

       [Fact]

       public void with_a_valid_student_id_selects_the_correct_student()

       {

             var sut = _provider.GetById<Student>(12345);

             sut.Should_Not_Be_Null();

             sut.Id.Should_Be_Equal_To(12345);

       }

}

Unfortunately, all that and I get an error:

creating_a_student.with_a_valid_student_id_selects_the_correct_student : Failed

XunitException: NHibernate.MappingException : No persister for: SIS.Domain.Student

After some debugging, the Sis.Domain.Student is being added to the Mapping collection, but I’m not sure if that’s the problem or not.  A Google of “no persister” pulls up a few NHibernate hits for not having the XML files set to Embedded Resource, which isn’t my problem here—is it?  I thought Fluent NHibernate escaped XMLHell.

I’d appreciate any guidance on this—I’m stumped.

Using Oracle ODP with NHibernate From a C# Class Library

November 7, 2008 2 comments

I’m working through a few demos today and needed to connect to an Oracle 10g database using NHibernate.  NHibernate, out of the box, has a Dialect for 10g/11g using the Oracle9Dialect; however, relies on Microsoft’s OracleClient library.  I prefer Oracle’s own ODP and, thankfully, after a few configuration settings, NHibernate complies. ๐Ÿ™‚

On the library with your mappings and domain classes, be sure that the hibernate.cfg.xml is set to Build Action: “Content” and Copy to Output Directory: “Copy always” when included in a class library (so it’s available in the /bin directory for the other consuming projects).

<?xml version=1.0 encoding=utf-8 ?>

<hibernate-configuration xmlns=urn:nhibernate-configuration-2.2>

       <session-factory>

              <property name=connection.provider>

                     NHibernate.Connection.DriverConnectionProvider

              </property>

              <property name=dialect>

                     NHibernate.Dialect.Oracle9Dialect

              </property>

              <property name=connection.driver_class>

                     NHibernate.Driver.OracleDataClientDriver

              </property>

              <property name=connection.connection_string>

                     User Id=user;

                     Password=password;

                     Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=
                           (PROTOCOL=TCP)(HOST=server)(PORT=1521)))
                           (CONNECT_DATA=(SERVICE_NAME=service.site)));

                     Pooling=true;

                     Enlist=false;

                     Statement Cache Size=50;

                     Min Pool Size=10;

                     Incr Pool Size=5;

                     Decr Pool Size=2;

              </property>

              <property name=show_sql>

                     true

              </property>

              <mapping assembly=NHibernateDemo.Core/>

       </session-factory>

</hibernate-configuration>

The connection.driver_class property value of NHibernate.Driver.OracleDataClientDriver tells NHibernate to look for the ODP (thanks Tomer!).  After wards, add in Oracle.DataAcess into the references of the project.  Unfortunately, there’s one more step.

If you try to compile and run tests at this point, you’ll get an NHibernate.HibernateException:

NHibernate.HibernateException : The IDbCommand and IDbConnection implementation in the assembly 
Oracle.DataAccess could not be found. Ensure that the assembly Oracle.DataAccess is located 
in the application directory or in the Global Assembly Cache. If the assembly 
is in the GAC, use <qualifyAssembly/> element in the application configuration file to 
specify the full name of the assembly.

The qualifyAssembly key goes in the App.config/Web.config file; however, I cannot get this to work using an App.config within the library itself.  For example, you’d need this qualifyAssembly key in EVERY application (tests, etc) that called your NHibernate Oracle objects.  Not cool.

For me, I’m using v2.111.6.20 of the ODP.NET for 11g.  Since I can’t get this working inside the class library, I’ll create and place it in the App.config of my Unit test project (demos with unit tests == good, console applications == bad).

<runtime>

       <assemblyBinding xmlns=urn:schemas-microsoft-com:asm.v1>

              <qualifyAssembly partialName=Oracle.DataAccess

                     fullName=Oracle.DataAccess,

                            Version=2.111.6.20,

                           Culture=neutral,

                           PublicKeyToken=89b483f429c47342 />

       </assemblyBinding>

</runtime>

Save, compile, run tests…

Successful XUnit Tests with NHibernate and Oracle ODP

Success!

Getting Oracle.DataAccess “Working” on x64

November 6, 2008 1 comment

Note: Please read to the end of the article before starting—results may vary.

We recently moved one of our applications off an older server to a shiny, speedy new server. In that transition, however, the new server was loaded with 64–bit Windows Server 2003 R2.  That’s fine—run .NET in 64–bit mode and kick along—my libraries are already set to run under ‘Any CPU’.

Wrong. BadFormatExceptions from Oracle.DataAccess.

Unfortunately, even the 64–bit versions of the Oracle 11g ODP returns a x86 Oracle.DataAccess.dll.  For me, setting the project to ‘x86’ (under the CPU build type) didn’t resolve the issue—it still couldn’t load the Oracle library.  A bit of hunting turned up that the x86 version of .NET isn’t installed by default.

Cool—easy fix.

To enable 32–bit access on a 64–bit platform, a few command lines are required:

First, a quick script to enable support on the system:

cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnWin64 True

Second, actually install the 32–bit version of ASP.NET onto IIS:

%SYSTEMROOT%\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis.exe -i

Finally, enable 32–bit ASP.NET inside the Web Server Extensions (IIS Management Console):

Enabling x86 .NET in IIS

Now, that works—but we have a 64–bit server downgrading everything to 32–bit.  Why can’t we find and get this crackin’ at 64–bit?  We just need a 64–bit version of Oracle.DataAccess, right? 

Right. ~~~ Like most thing, the challenge is in the details.

If you look at the GAC on a machine with ODP 64–bit installed (I’m using 11.1.0.6.21 for 11g), there isn’t a x64 image available.  To find the 64–bit edition, you need the 11g Release 1 Client (it’s release 1 today).  Unfortunately, it’s 11.1.0.6.0–-a different version. GAH.  Okay, go ahead and install!

It should install a x64 image library with the following properties:

Oracle Client 64-bit

Now, in the web.config for the application, we need to add an assembly redirection.

<dependentAssembly>

       <assemblyIdentity name=Oracle.DataAccess publicKeyToken=89b483f429c47342/>

       <bindingRedirect oldVersion=0.0.0.0-2.111.6.20 newVersion=2.111.6.0/>

</dependentAssembly>

Also, in the /bin directory of your project (or where ever your referenced libraries are), make sure that the 32–bit edition is not available so that the compiler will read from the GAC.

Start up the application and go.

Too easy, right?  Yeah, I’m afraid it is.  After all this work, performance under the 64–bit environment was atrocious.  A load test of about 1000 users (using the Visual Studio Web Test tools).

Here’s the graph (click for details—it’s big so the detail is available):

64-bit Performance Monitors <— 64–bit Output

The bouncing green line in the top graph is the JIT compiler recycling over and over while the red in the bottom is the CPU pegged… and not pegged after 1000 users, but after about 15 at :15 seconds into the run.  Ugh.  I finally cut it off at 7:15 as the system was getting unstable.

So, I removed the 64–bit calls, rolled back to calling 32–bit ODP, and ran the EXACT same load tests.

32-bit Performance <— 32–bit Output

In 32–bit mode, the CPU is running as expected (the spikes are the system generating 50–100 page PDF reports on the fly using DataDynamics ActiveReports) and we hit our full 1000+ concurrent users without any real issues or memory seepage.

As a quick test, I removed the references and the few lines that are accessing Oracle databases in the application and just used the 64–bit libraries for SQL Server 2005 and my own framework libraries—and performance screamed.  I’m pretty confident that this is within Oracle.DataAccess, but more investigation is required to nail down which objects/calls.  There may be more value of simply populating the few bits of data I’m pulling out of Oracle within our SQL environment and updating it daily—like psuedo, remove materialized views. ๐Ÿ™‚

So… while it’s good to know how to get the 64–bit libraries working, I’m VERY interested in why the performance was so poor.  I haven’t tossed ANTS at it yet in 64–bit mode, though that is the next test on a 64–bit development box that I’m building up.