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!

 

Be the first to rate this post

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

Tags:

Hibernate | Oracle | Spring.NET

Spring.NET AOP Cheat Sheet

by timvasil 3/31/2008 2:17:00 PM

Getting aspect oriented programming (AOP) working with objects in Spring.NET using the IoC container's XML configuration takes some concentration, since there's plenty going on--from defining the pointcut to coding up the advice to preparing the object factory that wraps the target in a proxy. 

Below is a relatively concise XML configuration to do all of these things:

    <object id="MyPointcutAdvisor" type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor, Spring.Aop">
        <property name="Advice">
            <object type="{ your interceptor type goes here, e.g. something that implements IMethodInterceptor }"/>
        </property>
        <property name="MappedNames">
            <list>
                <value>{ specify a method name to intercept here } </value>
                <value>{ specify a method name to intercept here } </value>
            </list>
        </property>
    </object>
    <object id="MyProxy" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
        <property name="Target">
            <object type="{ the wrapped target type goes here }"/>
        </property>
        <property name="InterceptorNames">
            <list>
                <value>MyPointcutAdvisor</value>
            </list>
        </property>
    </object>

Note that you can change NameMatchMethodPointcutAdvisor to something more robust like RegularExpressionMethodPointcutAdvisor if you need to intercept methods based on regular expressions rather than simple names.

To get a reference to the proxy object with advice in place, use ref="MyProxy" in the configuration XML for IoC hookup, or use this code:

MyType proxyObj = (MyType)ContextRegistry.GetContext().GetObject("MyProxy");

You can also wrap a pre-instantiated object at runtime:

MyType proxyObj = (MyType)ContextRegistry.GetContext().ConfigureObject(obj, "MyProxy");

Important note:  Spring.NET proxies use a decorator (inheritance) pattern or a composition pattern.  Neither approach allows advice on methods that are not interface implementations and not virtual!  Further, Spring.NET will not warn you or give you an error if it cannot configure advice for a non-virtual, non-interface method; instead, the methods will simply not be intercepted, leading to sometimes confusing bugs.  This issue is documented on the Spring.NET forums and bug tracker.

The decorator (inheritance) pattern is used when the target being proxied has no interfaces to proxy or ProxyTargetType is set to true on the proxy factory.  In this case the target type is subclassed and contains an internal pointer to the target object.  Otherwise the composition pattern is used, where a new class is defined dynamically implementing all the interfaces and likewise contains an internal pointer to the target object.

Currently rated 5.0 by 1 people

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

Tags:

Spring.NET | AOP

Using transaction attributes on non-IoC classes with Spring.NET

by timvasil 3/10/2008 6:32:00 PM

Spring.NET 1.1 makes managing transactions straightforward with the TransactionAttribute attribute.  For example:

        [Transaction(TransactionPropagation.Required)]
        public virtual void DoSomething()
        {
            // Do stuff here
        }

The details of what this means and how this works is fully documented.

It works great when you're using IoC features to configure objects.  But what if you want to slap TransactionAttribute on methods of classes you haven't pre-configured and whose lifetime you do not control, say for a methods on a unit test class (i.e. one with a [TestClass] attribute)? 

You can easily get the same kind of support by 1) defining a placeholder object in the Spring configuration, and 2) wrapping objects in proxies on the fly using this placeholder name.

Step 1:  Define a placeholder object

The configuration XML to get transaction attributes to work is a bit, well, verbose, as you can see below.  The red text shows the additions you can add to define a placeholder object called TransactionSupportedType.  You can use any name you wish.  The configuration basically says "objects named TransactionSupportedType should have methods with the TransactionAttribute intercepted."

    <!-- DAO -->
    <object id="Dao"
            type="TestApp.HibernateDao, TestApp"
            singleton="true">
        <property name="SessionFactory" ref="SessionFactory"/>
    </object>

    <!-- Transaction manager -->
    <object id="TransactionManager"
            type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
        <property name="DbProvider" ref="Provider"/>
        <property name="SessionFactory" ref="SessionFactory"/>
    </object>

    <!-- Transaction demarcation -->
    <object id="AttributeTransactionAttributeSource"
            type="Spring.Transaction.Interceptor.AttributesTransactionAttributeSource, Spring.Data">
    </object>
    <object id="TransactionInterceptor"
            type="Spring.Transaction.Interceptor.TransactionInterceptor, Spring.Data">
        <property name="TransactionAttributeSource" ref="AttributeTransactionAttributeSource"/>
        <property name="TransactionManager" ref="TransactionManager"/>
    </object>
    <object type="Spring.Transaction.Interceptor.TransactionAttributeSourceAdvisor, Spring.Data"
            autowire="constructor">
        <property name="TransactionInterceptor" ref="TransactionInterceptor"/>
    </object>
    <object id="Interceptors" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
        <property name="ObjectNames">
            <list>
                <value>Dao</value>
                <value>TransactionSupportedType</value>
            </list>
        </property>
        <property name="InterceptorNames">
            <list>
                <value>TransactionInterceptor</value>
            </list>
        </property>
    </object>
    <object id="TransactionSupportedType"/>

Step 2:  Wrap objects in proxies on the fly

Here's where we can easily leverage the AOP engine of Spring.NET to build the proxy we need-on-the fly.  It's as simple as:

MyType testProxy = (MyType)ContextRegistry.GetContext().ConfigureObject(this, "TransactionSupportedType");

This configures the preexisting object of type MyType with the configuration of TransactionSupportedType.  The proxy's public methods with the TransactionAttribute specified will be intercepted appropriately.  Be aware of an important gotcha:  the attribute works with public and/or interface methods only; you cannot use it with protected methods, even if they are marked virtual.  Advice on protected virtual methods may be available in Spring.NET 1.2 where in addition to composition and decorator proxies, inheritance proxies will be supported.

 

Be the first to rate this post

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

Tags:

Spring.NET | AOP

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.

Currently rated 4.5 by 2 people

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

Tags:

.NET Framework | Hibernate | Oracle | Spring.NET

 

About the author

Tim Vasil Tim Vasil
I'm a software engineer living in Cambridge, MA.

E-mail me Send mail

Search

Calendar

<<  March 2010  >>
MoTuWeThFrSaSu
22232425262728
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar

Recent comments