views:

291

answers:

3

I have a simple model class (Part), which pulls from it's information from a single table (t_Part).

I would like a subclass of this model called (ProducedPart), that would still utilize NHibernate's caching mechanisms, but would only be instances of (Part) that have a foreign key relationship in a table called "t_PartProduction". I do not need to have a model for this second table.

I only need a read-only version of ProducedPart

I could always implement a Facade/Repository over this, but I was hoping to setup a mapping that would pull "t_Part" joined with "PartProduction" when I asked for "ProducedPart" in NH.

Is this the wrong way to use NH?

Edit

So, the SQL would look something like

SELECT p.* 
FROM t_Part p 
INNER JOIN t_PartProduction pp ON pp.PartID = p.PartID 
WHERE pp.ProductionYear = '2009'
+1  A: 

No, this is perfectly possible. Look in the NHibernate documentation for the "table per subclass" model of inheritance. It will actually implement this as a LEFT JOIN, so that when you load a Part, it creates an instance of either your Part or your ProducedPart class depending on whether the other row is present. You'll find documentation on NHForge.

I'm not sure you could make ProducedPart read-only doing this though.

I'm assuming from this:

WHERE pp.ProductionYear = '2009'

that you want the subclass only where the production year is 2009, i.e. if when there is a record in t_PartProduction for a different year, you want this Part treated as a plain Part object, not a ProducedPart, then you could consider creating a view definition within your database that is a filtered version of t_PartProduction, then making your subclass join to this view rather than the base table.

David M
Thanks for the NHForge link. What about the "WHERE" clause of the class element?
Brett Veenstra
+1  A: 

I believe what you are looking for is a joined subclass. In FNH, it will look something like:

public class PartMap : ClassMap<Part>
{
    public PartMap()
    {
        Id(x => x.Id)

        JoinedSubClass<ProducedPart>("PartID", sub => { 
            sub.Map(x => x.Name); 
            sub.Map(x => x.ProductionYear); 
        });
    }
}

In order have NHibernate cache the results, you will need to have the subclass mapped (and if you didn't map it, you wouldn't be able to get NH to load it in the first place).

Bringing in some context from the FNH groups thread, it will not explicitly be read-only though. In my opinion, making things read-only is not an appropriate thing for NHibernate to manage. This is better controlled by the database and connections (i.e. creating a connection to the database that only has SELECT permissions on the tables/views being accessed). See my answer to a previous SO question about readonly sessions in NHibernate for more of my thoughts on the matter.

Stuart Childs
Doesn't FNH have a ReadOnly() method to make the Model read-only?
Brett Veenstra
Yes, and as you discovered it sets the mutable attribute to false. This may be fine if your application is the only one that touches the database or you don't have a strong *need* to keep the data read-only. However, as long as you are using a fully privileged connection to the database, any code can use that connection go around NH's speed bump and execute any CRUD statements it wants. This is what I was getting at in my other answer; it's fine (good design, even) to suggest read-only through your API usage but if there is the need, you should *enforce* it at the appropriate level.
Stuart Childs
ProducedPart as read-only is really just a construct of looking at the data. It's essentially creating a SQL `view` of Parts that have Production for the current year, which is a very helpful idiom for my project. Since this model is basically virtual, I wanted to be sure and make it immutable as well. Thanks!
Brett Veenstra
I think I see where you are going with this now; sorry if I got off on a tangent. Just as an alternative, what about have a PartRepository or ProducedPartRepository that has methods (named to your liking) like GetProducedPartsForYear(int year) and GetProducedPartsForCurrentYear()? I suggest this only because ProducedPart is then a true part of your domain and the methods give you historical access to parts from previous years. Ultimately, of course, it depends on what exactly you're building and how you need your database to work.
Stuart Childs
A: 

The key here is using both the where and mutable elements of the class definition for NHibernate Mappings.

Using Fluent NHibernate, this looks like:

    public Part()
    {
        WithTable("t_Part");

        Id(i => i.Id).ColumnName("PartID");
        Map(m => m.Name).ColumnName("Part");

        SetAttribute("where", "PartID IN ( SELECT pp.PartID FROM t_PartProduction pp WHERE pp.ProductionYear = '2009' ) ");

        ReadOnly();
    }
Brett Veenstra