Showing posts with label NHibernate. Show all posts
Showing posts with label NHibernate. Show all posts

Wednesday, January 06, 2010

Custom column names in Fluent NHibernate

In working on our web application, I came across an interesting issue with our Fluent NHibernate mapping files. On property mappings where we specified a column name (using the .Column("columnName") property), all of the other attributes that we applied to the property (length, nullability, lazyloading, etc) were ignored. This has been documented in here. I needed to move forward with this, so I wrote a simple extension method to handle our scenario.

This extension method is used in place of the Column("columnName") method call and doesn't replace the other values. Instead, it uses reflection to update the defaultColumn field's name value, just as the Length(), Nullable() and other methods do.

public static PropertyPart ColumnName(this PropertyPart part, string columnName)
        {
            const BindingFlags accessor = BindingFlags.NonPublic | BindingFlags.Instance;

            // retrieve the defaultColumn field property
            var defaultColumnField = 
                typeof(PropertyPart).GetField("defaultColumn", accessor);

            // get the current value of the field
            var defaultColumn = (ColumnMapping)defaultColumnField.GetValue(part);
            
            // set the column name as appropriate
            defaultColumn.Name = columnName;

            // reset the default column with the applied column name
            defaultColumnField.SetValue(part, defaultColumn);

            return part;
        }

I am still not sure why the Fluent guys decided to update the column name property the way that they did, but this seems to do the trick for us. As a result, I have replaced all Column() calls with ColumnName() calls, to prevent these unexpected behaviors in the future.

One risk with this solution, and it is considerable, is that I am binding to the internals of the PropertyPart class, which is subject to change out from under me.

Now, I just need to make sure I investigate the source before deploying a new build to ensure that this won't break my workaround.

What do you think?

Tuesday, October 28, 2008

NHibernate one-to-one mapping does an update on the parent when the child is created..

I have been implementing new functionality to an existing system using NHibernate. This application has an existing schema that we need to augment. In the current project I have an existing database that is used for transaction tracking. The main table for tracking transactions is Transaction. This table has an InsteadOf trigger on the table that prevents updates when the transaction has been cancelled or posted. I am adding functionality to the system to show the calculate the transaction sequence number for each transaction for the given account. This sequence is the incrementing count of all non-cancelled transactions, in date order. If a transaction within the sequence is cancelled, all transaction sequences are recalculated and the TransactionSequence table is updated with the results. The cancelled transaction will retain its sequence number, but the next sequence number will be decremented until there is a continuous sequencing of all non-cancelled transactions.

To implement this, I created a TransactionSequence table that has a primary key of TransactionId which is also a foreign key to the Transaction table. I then created a one-to-one mapping between these two entities in my mapping files.

The Transaction mapping file looks like this:

<class name="Transaction" dynamic-update="true">
<id name="Id" column="ID">
<generator class="native" />
id>
<property name="TransactionTypeId" access="field.camelcase-underscore" />
<property name="TransactionStatusId" column="DebitDebitStatus" access="field.camelcase-underscore" />

<one-to-one name="Sequence" class="TransactionSequence" fetch="join"
lazy="false" constrained="false">
<
/one-to-one>
class>

The TransactionSequence mapping looks like:

<class name="TransactionSequence" table="TransactionSequence" dynamic-update="true">
<id name="TransactionId" column="TransactionID" type="Int32">
<generator class="foreign">
<param name="property">Transactionparam>
generator>
id>
<version name="Version" column="Version" unsaved-value="-1" access="field.camelcase-underscore" />
<property name="SequenceNumber" not-null="true" />
<one-to-one name="Transaction"
class="Transaction"
constrained="true"
foreign-key="fk_Transaction_Sequence" />


When I have calculate the transaction sequences, I do a save of the Transaction within the NHibernate session, but it fails when the transaction sequence does not yet exist because it tries to update the Transaction table with a query like 'UPDATE Transaction SET Id = ? WHERE Id = ?' with the same value for parameters 1 and 2.

How can I get NHibernate to create the child of the one-to-one mapping without updating the Transaction record? Has anyone seen this before?