System.BadImageFormatException using NHibernate proxies

by timvasil 4/24/2008 11:36:00 AM

I made a bunch of changes to code recently and then ran a unit test to find this:

NHibernate.HibernateException: Creating a proxy instance failed --->  System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. --->  System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B).

This happened when I tried to serialize an object graph using WCF and my handy NHibernate data contract surrogate. Strangely, if I set breakpoints in certain places the exception would not occur but proxies elsewhere would behave strangely, e.g. lose property values.

Poking around on the Castle DynamicProxy forums, there were mentions of this being due to an abstract class being proxied.  I turned off lazy initialization of classes that had abstract bases, and yet the problem persisted.  Then, on a clue that DynamicProxy lacked support for proxying generic classes, I removed the "virtual" keyword from what looked like an innocuous method:

        protected virtual IEnumerable<T> GetSubset<T>()
        {
            . . .
        }

Simply removing the "virtual" keyword fixed the problem.  So be warned:  no generic methods on your proxy classes, at least not until DynamicCastle 2.0!

Tim

Tags:

Hibernate | Castle.DynamicProxy

Using Spring.NET 1.1.1 with Oracle - Your driver must be this tall

by timvasil 4/23/2008 1:19:00 PM

When I recently upgraded from Spring.NET 1.1.0 to 1.1.1, I started getting a bizarre message:

DbProvider product name = Oracle, Oracle provider V2.102.2.20, could not resolve commandBuilderDeriveParametersMethod DeriveParameters to MethodInfo

It's looking for a method named DeriveParameters on the Oracle.DataAccess.Client.OracleCommandBuilder class.  Poking around with the .NET reflector, the method, sure enough, is not there.

After looking at some forum posts and a logged defect against Spring.NET, there were workarounds.  But surely with a database as common as Oracle these steps shouldn't be necessary!  No one having the issue was using Oracle, and, as it turns out, it's because they're using a newer driver that defines this magical DeriveParameters method.

I was using version 10.1.0.200, which is much newer than the 2.102.2.20 that Spring.NET expects when you look at the dbproviders.xml file.  But, alas, 10.1.0.200 is apparently code for something pre-2.102.2.20.  Oracle has two versioning schemes.  I found that when I downloaded the latest Oracle data access components (ODAC), billed as 11.1.0, I ended up with an Oracle.DataAccess.dll with a version 2.116.6.20 timestamp.  Happily, I found this to work with Spring.NET 1.1 without issue.

As a side note, I upgraded to NHibernate 1.2.1 from 1.2.0 at the same time and found the Oracle dialect suddenly dropped support for the "guid" data type.  Moral of the story:  even point releases have gotchas!

 

Tags:

Hibernate | Oracle | Spring.NET

NHibernate & Spring.NET with Oracle

by timvasil 2/14/2008 1:50:00 PM
The documentation for using NHibernate with Oracle is a bit spotty.  Based on some internet searches and trial & error, here's what I've found to work:
  1. Download the Oracle client (or "instant client"):
     
    http://www.oracle.com/technology/software/products/database/oracle10g/htdocs/winsoft.html
     
    Without this client, you'll see the following error message when using Microsoft's Oracle driver:  Class Initialization method Imd.Tests.ServerApi.DeploymentTest.Initialize threw exception. NHibernate.HibernateException:  NHibernate.HibernateException: System.Data.OracleClient requires Oracle client software version 8.1.7 or greater. --->  System.Exception: System.Data.OracleClient requires Oracle client software version 8.1.7 or greater.
     
  2. Set the ORACLE_HOME environment variable and restart your IDE.  An example value is C:\Oracle\product\10.1.0\Client_1. If you don't set this variable you get an error message stating:
     
    Unable to load DLL
    'OraOps10.dll': The specified module could not be found
     
     
    If you specify the wrong directory, i.e. specify a directory one level too high or too low, you'll see bizarre exceptions, including NullReferenceExceptions circa Oracle.DataAccess.Client.OracleException.get_Source() or Oracle.DataAccess.Client.OracleException.get_Message().
      
  3. Add a project assembly reference to Oracle.DataAccess.dll.  I found this in C:\Oracle\product\10.1.0\Client_1\BIN.
     
  4. Update your App.config (or Web.config) properties to specify the Oracle connection string, driver, dialect, etc:

        <db:provider provider="OracleClient-2.0"
                     connectionString="Data Source=(DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = {hostname})(PORT = 1521)))(CONNECT_DATA = (SERVICE_NAME = {serviceName})));User Id={username};Password={pass};"/>
     
        <object id="ConfigDbSessionFactory"
                type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12">
            <property name="HibernateProperties">
                <dictionary>
                    <entry key="hibernate.connection.provider" value="NHibernate.Connection.DriverConnectionProvider"/>
                    <entry key="hibernate.dialect" value="NHibernate.Dialect.Oracle9Dialect"/>
                    <entry key="hibernate.connection.driver_class" value="NHibernate.Driver.OracleClientDriver"/>
                </dictionary>
            </property>
           . . .
        </object>

These instructions are for using Microsoft's .NET driver (System.Data.OracleClient / V2.0.0.0).  A few settings would have to change if you want to use Oracle's .NET driver driver instead (System.DataAccess.Client / V2.102.2.20):

  1. Change OracleClient-2.0 to OracleODP-2.0.
  2. Change NHibernate.Driver.OracleClientDriver to NHibernate.Driver.OracleDataClientDriver

According to the NHibernate documentation, "Microsoft's driver does not handle long character strings correctly. An error happens in some circumstances when using a string of length 2000-4000 as a parameter value.  Oracle cannot handle empty strings (""), you should use null instead. An IUserType implementation to perform the conversion is contained in Nullables.NHibernate library (part of NHibernateContrib package)."  I've also seen problems with the Microsoft driver not being able to convert a Guid into a byte[].  So pick your poison :-)

Common Pitfalls:
  • You may recieve a perplexing "invalid username/password" error when your username and password is, in fact, correct.  ODP.NET might be adjusting the case of your password before hashing it and sending it to Oracle; you can get around this by turning off case sensitivity:
    ALTER SYSTEM SET SEC_CASE_SENSITIVE_LOGON = FALSE;
  • You may see NullReferenceExceptions with Oracle.DataAccess.Client.OracleException.get_Source() in the stack trace.  This may be indicative of a mismatch between the Spring.NET provider name and the NHibernate driver, i.e. using an Oracle provider name and a Microsoft driver.  If you specify OracleClient-2.0, use the NHibernate.Driver.OracleDataClientDriver driver; if you specify OracleODP-2.0, use NHibernate.Driver.OracleClientDriver.

Tags:

.NET Framework | Hibernate | Oracle | Spring.NET

WCF serialization with NHibernate object graphs

by timvasil 2/5/2008 11:05:00 AM

Say you have a complex object graph with circular references and you need to send it across the wire as XML.  The good ol' XmlSerializer is not going to help you; you need a more powerful too like WCF's DataContractSerializer.  By slapping [DataContract] onto the classes in your object graph and [DataMember] onto the non-transient properties and fields of these classes, you're golden.  WCF serializes cyclical object graphs without complaint.  Well, almost.

If you're using Hibernate's lazy-initialization feature, you'll see a problem:  .NET will compain about unknown types (proxy wrappers) that need to be registered as well-known types.  Unfortunately the types aren't known until run-time.  Sure, you could traverse the object graph at runtime, figure out these proxy types, and pass them into a DataContractSerializer costructor, but that's far from elegant.  Even if you make it over that hurdle, .NET will complain about Hibernate's persistent set implementations too; you'll have to add those types too.  If you're using generics, that could quickly balloon to a lot of types.

Fortunately there's a straightforward solution:  IDataContractSurrogate.  WCF provides this interface specifically to get around sticky situations like this.  You can use it to unwrap and initialize proxies, and to replace Hibernate's persistent collections with built-in well-known collections. Here's how it works:

public class HibernateDataContractSurrogate : IDataContractSurrogate
{
    public HibernateDataContractSurrogate()
    {
    }

    public Type GetDataContractType(Type type)
    {
        // Serialize proxies as the base type
        if (typeof(INHibernateProxy).IsAssignableFrom(type))
        {
            type = type.GetType().BaseType;
        }

        // Serialize persistent collections as the collection interface type
        if (typeof(IPersistentCollection).IsAssignableFrom(type))
        {
            foreach (Type collInterface in type.GetInterfaces())
            {
                if (collInterface.IsGenericType)
                {
                    type = collInterface;
                    break;
                }
                else if (!collInterface.Equals(typeof(IPersistentCollection)))
                {
                    type = collInterface;
                }
            }
        }

        return type;
    }

    public object GetObjectToSerialize(object obj, Type targetType)
    {
        // Serialize proxies as the base type
        if (obj is INHibernateProxy)
        {
            // Getting the implementation of the proxy forces an initialization of the proxied object (if not yet initialized)
            obj = NHibernateProxyHelper.GetLazyInitializer((INHibernateProxy)obj).GetImplementation();
        }

        // Serialize persistent collections as the collection interface type
        if (obj is IPersistentCollection)
        {
            IPersistentCollection persistentCollection = (IPersistentCollection)obj;
            persistentCollection.ForceInitialization();
            obj = persistentCollection.Entries(); // This returns the "wrapped" collection
        }

        return obj;
    }

    public object GetDeserializedObject(object obj, Type targetType)
    {
        return obj;
    }

    public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
    {
        return null;
    }

    public object GetCustomDataToExport(Type clrType, Type dataContractType)
    {
        return null;
    }

    public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
    {
    }

    public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
    {
        return null;
    }

    public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
    {
        return typeDeclaration;
    }
}

To use this surrogate, simply pass in an instance to the DataContractSerializer constructor:

DataContractSerializer serializer = new DataContractSerializer(typeof(rootObj.GetType()), null, int.MaxValue, false, true, new HibernateDataContractSurrogate());
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, rootObj);

Notes:

  • With this technique, you avoid having to translate your Hibernate objects into DTOs before serialization.
  • There's an alternative:  you can use NetDataContractSerializer, however that would assume you've got NHibernate and Castyle.DynamicProxy at the other end of the wire.  It also requires a bit more legwork, as described here.
  • WCF doesn't know about Iesi sets, so you're still on the hook for adding attributes such as [WellKnown(typeof(HasedSet<type>))] to classes that use sets.

Tags:

.NET Framework | Hibernate | WCF

ResultSets vs. Hibernate

by timvasil 2/1/2008 8:14:00 PM

When writing Java code requiring object/relational mapping to an underlying relational database, I've always relied on Hibernate.  Recently, though, I tackled a small project dealing with only a few objects and primarily read-only access to the data.  I thought it'd be the perfect opportunity to use JDBC directly--to keep the project simple.  The code ended up looking pretty clean.  Here's a sample data access method to log in a user and provide some information related to the user, namely the Customer object associated with the user and all the "Sites" associated with that customer:

try
{
    final NamedParameterStatement stmt = new NamedParameterStatement(getConnection(),
            "SELECT u.*, c.*, cs.*, us.CustomerSiteId AS UserSiteId FROM tbl_users u" +
            " INNER JOIN tbl_customers c ON u.CustomerId = c.CustomerId" +
            " INNER JOIN tbl_customer_sites cs ON cs.CustomerId = c.CustomerId" +
            " LEFT OUTER JOIN tbl_user_sites us ON us.CustomerSiteId = cs.CustomerSiteId" +
            " WHERE LOWER(Username) = :username AND (:password IS NULL OR u.Password = :password)");
    stmt.setString("username", StringUtils.nonNullify(username).toLowerCase());
    stmt.setString("password", password);
    final ResultSet results = stmt.executeQuery();
    if (!results.first())
    {
        // No user found
        return null;
    }

    // Hydrate customer
    final Customer customer = HydrationUtils.hydrateCustomer(results);
   
    // Hydrate user
    final User user = HydrationUtils.hydrateUser(results);
    user.setCustomer(customer);
   
    // Hydrate customer sites and determine user accessible sites
    results.beforeFirst();
    while (results.next())
    {
        final Site site = HydrationUtils.hydrateSite(results);
        customer.addSite(site);
        if (results.getObject("UserSiteId") != null)
        {
            user.getAccessibleSites().add(site);
        }
    }
   
    if (updateLastLogonTime)
    {
        final NamedParameterStatement updateStmt = new NamedParameterStatement(getConnection(),
                "UPDATE tbl_users SET LastLogon = NOW() WHERE UserId = :userId;");
        updateStmt.setInt("userId", user.getId());
        updateStmt.executeUpdate();
    }
   
    return user;
}
catch (Exception e)
{
    s_log.error(e);
    throw new RuntimeException(e);
}
finally
{
    close();

There are a number of things to notice about this code:

  • Java doesn't support named parameters.  I had to write my own NamedParameterStatement class so I could perform parameter substitution using named parameters.
  • Though you don't see its implementation, my getConnection() method pulls a connection from a c3p0 connection pool.  Without Hibernate, I have to manage connection strings and the connection pool.
  • The "hydration" process (aka relational to object mapping) is handled by the HydrationUtils class.  This is a bit clunky and an annoyance to write, with a bunch of setter calls and data conversions (e.g. from java.sql.Date to java.util.Date).
  • The work to ensure there aren't multiple objects referring to the same row in a table is handled explicitly, i.e. the sites in the user's access list is pulled from the sites in the customer's list.  Hibernate would have handled this for me.
  • The underlying DDL script has to be written by me, and the embedded SQL limits portability.

Yes, there's a bit of work going on here.  Some of it is brittle.  For example, additions to the object model will not cause compile time errors, yet the HydrationUtils methods would need updating.  Such issues would likely not be caught until runtime.

As the application grew more complex and I found myself struggling to manage unique indexes and foreign keys in an ever-growing DDL script, I decided to switch to Hibernate.  Now, take a look at the same user logon code written Hibernate style:

return new TransactionRunner<User>() {
    @Override
    protected User doWork(Session session) throws Exception
    {
        // Prepare the user-fetching query--eagerly fetching associated customer and the customer's associated sites
        final Query userQuery = session.createQuery("from User as u " +
          " inner join fetch u.customer c" +
          " inner join fetch c.sites" +
          " left join fetch u.accessibleSites" +
          " where lower(u.username) = :username and (:password is null or u.password = :password)");
        userQuery.setParameter("username", username);
        userQuery.setParameter("password", password);
       
        // Fetch the user
        final User user = (User)userQuery.uniqueResult();
       
        // Update the user's last logon time (if so desired)
        if (updateLastLogonTime)
        {
            user.setLastLogon(new Date());
            session.flush();
        }
       
        // Replace persistent sets with regular sets for serialization
        user.setAccessibleSites(new HashSet(user.getAccessibleSites()));
        user.getCustomer().setSites(new HashSet(user.getCustomer().getSites()));
        session.setFlushMode(FlushMode.MANUAL);
        return user;
    }
}.run();

See the difference?

  • I wrote HQL instead of SQL to ensure portability.  No more NamedParameterStatement; Hibernate understands named parameters.  It also understands objects, so the query has an object-oriented feel to it.  I'm joining objects, not IDs.
  • There's no hydration code.  Hibernate does this for me.
  • Updating an object is as simply as changing its properties; I didn't have to write any SQL.
  • I didn't have to manage any databsae connection object explicitly.
  • I did write a TransactionRunner class to wrap the code in a transaction, so the begin/commit/rollback could happen at the appropriate time.  Writing data access methods within anonymous classes is a bit clunkly.  The alternative of using annotations with a pointcut-enabling component such as Spring crossed my mind, but I don't really have any need for most of its features at this point so I'll live with the callback.
  • I have to jump through a few hoops near the end of the method so I can get rid of Hibernate's persistent collections in favor of Java's built-in collections.  Even though Hibernate's collections implement the standard Java List, Map, and Set interfaces, they don't play nice with GWT, and I needed to serialize the object graph to a web client.

Overall I feel more comfortable with the Hibernate code.  I think it's cleaner and easier to maintain.  And next time I'm working on a simple little project, I won't be so quick to discount an O/R mapping tool; I believe it does save time, even for the small projects.

Tags:

Java | Hibernate | GWT

Search

Calendar

«  May 2013  »
SuMoTuWeThFrSa
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar

Recent posts

Recent comments

Archive