views:

114

answers:

3

Let's say my domain looks like this:

  • I have an object, Vehicle, that has an OdometerReading property.
  • An OdometerReading has the Miles & Date (when it was read).

I need to keep a history of all OdometerReadings for the Vehicle in the database, but don't want the entire odometer history to belong to the Vehicle object. What I would like is for the OdometerReading property map to the most recent OdometerReading entry out of the database.

I thought about mapping the whole collection of OdometerReadings to the Vehicle, and having a dynamic property called CurrentOdometerReading that would order them and return the latest one, but I don't need the whole collection under the Vehicle in my domain, and it seems like I would be getting more data out of the database than I need.

Is that possible with NHibernate? How would I map such a thing?

A: 

There are a few ways of doing this depending on what you want your domain model to look like. My preferred choice is to use a custom collection type, for example IOdometerReadingCollection, which you can add extra methods to. In this case it might be something like:

public interface IOdometerReadingCollection : IList<OdometerReading>
{
    OdometerReading Latest { get; }
}

This way you can do:

OdometerReading reading = vehicle.OdometerReadings.Latest;

which I prefer to:

OdometerReading reading = vehicle.LatestOdometerReading;

There's lots of documentation about custom collections, which you can find with a simple google search.

If this approach isn't for you there are other options. You may be able to use a property with a formula (I'm not sure if that works with complex types?), or a regular NHibernate association where you'd have the key of the latest OdometerReading on your Vehicle mapping. As you also mentioned you could just load all the OdometerReadings, which depending on your use case might actually be fine.

I hope this helps, or at least points you in the right direction.

jonnii
Thanks, jonnii. This would still leave me with a collection of OdometerReadings. I want there to be a single OdometerReading on the Vehicle. Is there a way to only have ONE OdometerReading, and have it be the latest one?
Brad Mellen-Crandell
This way you can have access to all the odometer readings if you want them as well as being able to get just the latest one. If you ONLY want the latest one you're best bet is to use what epitka suggested (a where clause).
jonnii
Regardless of what solution you choose, is it at all possible to order a collection of vehicles (using the criteria api) by the vehicle.LatestOdometerReading or vehicle.OdometerReadings.Latest?
Kristoffer Ahl
That depends on how you represent the OdometerReadings. If they're just, for example, integers you can store them as part of the vehicles table, which you can then sort by. If you want to bring back the entire collection and also have it pre-sorted by the db that's going to be a little trickier. Depending on the number of vehicles, you might be better off doing it in memory.
jonnii
+1  A: 

There is a "where" clause that you can put in your collection mapping. Check the reference documentation.

epitka
This would give me a collection still, just one with a single result, wouldn't it? I don't want the OdometerReadings to be a collection at all.
Brad Mellen-Crandell
Nobody is forcing you to expose that as a collection, you can have as a private field and then have mapped property private decimal _odometerReadings;public decimal LastOdometerReading{get { return _odometerReadings.FirstOrDefault(); }}
epitka
+1  A: 

I would map the OdometerReading property as a component, then use a named query to ensure it's populated with the latest reading out of the database. (In this example, you'd have a sql-query with a name of "vehicle" that does the SQL to load the Vehicle columns along with the latest Odometer reading)

<class name="Vehicle">
    <property name="Type" not-null="true"/>
    <component name="OdometerReading">
        <property name="Miles" />
        <property name="Date" />
    </component>
    <loader query-ref="vehicle"/>
</class>
Kirk Clawson