Further to this question:
Composition over Inheritance - where do extra properties go?
The accepted answer and similar ones answer this nicely. But to take this further, what if the Sales department and Production department wanted to record different information about Sickness and Holiday absences? This could be one solution:
public class Holiday : Absence
{
//Extra fields go here.
}
public class Sickness : Absence
{
//Extra fields go here.
}
public class SalesHoliday : Holiday
{
//Extra fields go here.
}
public class SalesSickness : Sickness
{
//Extra fields go here.
}
public class ProductionHoliday : Sickness
{
//Extra fields go here.
}
public class ProductionSickness : Sickness
{
//Extra fields go here.
}
clearly, this is the start of a class explosion which will only get worse, and therefore should be avoided.
One possible solution would be to use the Decorator Pattern (Gang of Four). This would be ideal but in this hypothetical example, the persistence is with NHibernate. I have looked all over the place for an example of how to map the Decorator pattern in NHibernate and not found anything. My experiments have, and one point, utilised various combinations of subclass mappings, joined-subclass mappings, union-subclass mappings, discriminators, implicit polymorphism and many-to-any mappings, but so far with no satisfactory results. Has anyone cracked this one? An Employee entity would have a collection of absences of any type so polymorphic behavior is a requirement.
In modeling this particular type of application I would try to stay away from sub-classing an absence based on department. Instead, I would try to make the absence handling system capable of supporting any department. You can devise a model that allows adding custom properties to an absence if that becomes necessary. This could be done using either a generic notes field or a dictionary. This data could then be further structured by a given context, such as Sales and Production.
One way to think of this is that an absence in the sales department is much like an absence in the production department, so the 'type' of the entity does not change. What may change is particular details about an absence, which in turn warrants the composition over inheritance approach.
I finally answered my own question. Union-subclass is the way forward, like in this pizza example:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Decorator.Domain.Entities"
assembly="Decorator.Domain">
<class name="IPizza" abstract="true">
<id name="Id" column="Id" type="guid">
<generator class="assigned"/>
</id>
<many-to-one name="Order" class="Order" column="`OrderId`" cascade="save-update" />
<union-subclass name="Pizza" table ="`Pizza`" >
<property name="Size" column="`Size`" />
<property name="Cheese" />
<property name="Tomato" />
</union-subclass>
<union-subclass name="PepperoniDecorator" table ="`PepperoniDecorator`" >
<many-to-one name="BasePizza" class="IPizza" column="`BasePizzaId`" cascade="all" />
<property name="ExtraSpicy" column="`ExtraSpicy`" />
</union-subclass>
<union-subclass name="OliveDecorator" table ="`OliveDecorator`" >
<many-to-one name="BasePizza" class="IPizza" column="`BasePizzaId`" cascade="all" />
<property name="Colour" column="`Colour`" />
</union-subclass>
</class>
</hibernate-mapping>
I go in to this in more detail on my blog:
http://lucidcoding.blogspot.co.uk/2013/07/mapping-decorator-pattern-in-nhibernate.html
Related
I have read a post on the internet (I can no longer find that post for me to refeence) that a Many-To-Many relationship can be replaced with a one-to-many relationship. Can someone provide an example?
I just come up to that question, and realized, that there is missing any answer. And it is a shame, while I do often point out this NHibernate documentation statement: 24. Best Practices
Don't use exotic association mappings.
Good usecases for a real many-to-many associations are rare. Most of
the time you need additional information stored in the "link table".
In this case, it is much better to use two one-to-many associations to
an intermediate link class. In fact, we think that most associations
are one-to-many and many-to-one, you should be careful when using any
other association style and ask yourself if it is really neccessary.
Take a look at the example under the 23.2. Author/Work. Extract, the simplified version of the many-to-many relation between Author and Work:
<class name="Work" table="works" ...>
<id name="Id" column="id" generator="native" />
...
<set name="Authors" table="author_work" lazy="true">
<key>
<column name="work_id" not-null="true"/>
</key>
<many-to-many class="Author">
<column name="author_id" not-null="true"/>
</many-to-many>
</set>
</class>
And its many-to-many target Author:
<class name="Author" table="authors">
...
<set name="Works" table="author_work" inverse="true" lazy="true">
<key column="author_id"/>
<many-to-many class="Work" column="work_id"/>
</set>
</class>
So, if we would like to order the set of Works on load, we do have a problem. There is no column in the pair table. But what's more important, there is no way how to manage such a column.
What we can do, is to introduced the Pair object: AuthorWork and extend the Pair table as needed
public class AuthorWork
{
public virtual Author Author { get; set; }
public virtual Work Work { get; set; }
public virtual int OrderBy { get; set; }
}
Mapping of the AuthorWork
<class name="AuthorWork" table="author_work">
...
<many-to-one name="Author" column="author_id" />
<many-to-one name="Workr" column="work_id" />
<property name="OrderBy" />
Having this we can convert the many-to-many mapping to one-to-many, for example the Authors collection:
<set name="Authors" lazy="true"
order-by="OrderBy">
<key column="work_id" not-null="true"/>
<one-to-many class="AuthorWork" />
</set>
And we can manage the entity AuthorWork, set the OrderBy column, and therefore effectively work with the pairing table.
NOTE: have to agree with that suggestion in docsumentation The more requirements come, the more happy we are that we do have a way how to manage the relation!
Let's say that I have and database table called People, and entity People. Let's say that I need a quick way to remove a subset of people from displaying everywhere in application. So I add IsDeleted column to People table, and set values in that column to 1.
In Entity Framework there's a mechanism that specifies that for instans of entities with value in column IsDeleted set to 1 shouldn't be fetched and mapping framework filters data automatically. Is is possible to achieve with NHibernate?
You can define where attribute in your class mapping.
where (optional) specify an arbitrary SQL WHERE condition to be used
when retrieving objects of this class
<class ... where="IsDeleted = 0">
If you are using Fluent NHibernate then just define this in mapping class:
Where("IsDeleted = 0");
NH's mapping by code should be similar to Fluent NHibernate's mapping.
You can create abstract class, e.g. PeopleBase, from which your People class will be derived and map your entity to it.
After that you can use discriminator like this (didn't check for correctness, but it should work):
<class name="YourNamespace.PeopleBase,YourNamespace" table="People">
// ...
<discriminator column="IsDeleted" type="boolean" />
// Properties...
// ...
<subclass name="YourNamespace.People, YourNamespace" discriminator-value="false">
</subclass>
</class>
In order to achieve what I wanted, I've created base class + two subclasses. This is the configuration:
subclasses with discriminator-value:
<subclass name="People" discriminator-value="null">
</subclass>
<subclass name="PeopleHistory" discriminator-value="not null">
<property name="MasterRowId" />
</subclass>
discriminator in base class:
<discriminator column="MasterRowId" />
I have an interface IUserLocation and a concrete type UserLocation.
When I use ICriteria, specifying the interface IUserLocation, I want NHibernate to instantiate a collection of the concrete UserLocation type.
I have created an HBM mapping file using the table per concrete type strategy (shown below). However, when I query NHibernate using ICriteria I get:
NHibernate cannot instantiate abstract class or interface MyNamespace.IUserLocation
Can anyone see why this is? (source code for the relevant bit of NHibernate here (I think))
My ICriteria:
var filter = DetachedCriteria.For<IUserLocation>()
.Add(Restrictions.Eq("UserId", userId));
return filter.GetExecutableCriteria(UoW.Session)
.List<IUserLocation>();
My mapping file:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="true">
<class xmlns="urn:nhibernate-mapping-2.2" name="MyNamespace.IUserLocation,MyAssembly" abstract="true" table="IUserLocations">
<composite-id>
<key-property name="UserId" column="UserId" type="System.Guid"></key-property>
<key-many-to-one name="Location" column="LocationId" class="MyNamespace.ILocation,MyAssembly"></key-many-to-one>
</composite-id>
<union-subclass table="UserLocations" name="MyNamespace2.UserLocation,MyAssembly2">
<property name="IsAdmin" />
</union-subclass>
</class>
</hibernate-mapping>
From the documentation it looks like your mapping file should do it to me. I've never tried table per concrete class though. And I notice the examples in the NHibernate documentation for it don't use interfaces for the base class. Perhaps it's not supported?
I have used Table per concrete class, using implicit polymorphism before with a separate mapping for each subclass.
<class name="MyNamespace.UserLocation,MyAssembly" table="UserLocations">
...
</class>
It seems that NHibernate needs to have an id tag specified as part of the mapping. This presents a problem for views as most of the time (in my experience) a view will not have an Id. I have mapped views before in nhibernate, but they way I did it seemed to be be messy to me.
Here is a contrived example of how I am doing it currently.
Mapping
<class name="ProductView" table="viewProduct" mutable="false" >
<id name="Id" type="Guid" >
<generator class="guid.comb" />
</id>
<property name="Name" />
<!-- more properties -->
</class>
View SQL
Select NewID() as Id, ProductName as Name, --More columns
From Product
Class
public class ProductView
{
public virtual Id {get; set;}
public virtual Name {get; set;}
}
I don't need an Id for the product or in the case of some views I may not have an id for the view, depending on if I have control over the View
Is there a better way of mapping views to objects in nhibernate?
Edit
Answer So Far
Mapping
<class name="ProductView" table="viewProduct" mutable="false" >
<id name="Id" type="Guid" />
<property name="Name" />
<!-- more properties -->
</class>
Class
public class ProductView
{
public virtual Name {get; set;}
//more properties
}
View SQL
Do I still need NewID()?
Select NewID() as Id, ProductName as Name, --More columns
From Product
You can make it just a little bit cleaner by not mapping the Id to a property and omitting the generator:
<id column="Id" type="guid"/>
That way, you keep the problem in the data layer, without leaking the implementation detail to your domain.
As far as I know, NHibernate will require either an id or a composite-id definition since it's the mechanism by which it uniquely identifies a given record. If there is no combination of columns that provides a key for each row in the view, I think you are stuck with hacky workarounds.
I'm using the NHibernate criteria API to load some entities from my database. My domain consists of an abstract class, Animal, which the concrete Cat class inherits from. Another concrete class, Tiger, inherits from Cat.
I need to load all Cats from the database, so I'm doing the following-
ICriteria criteria = session.CreateCriteria(typeof(Cat));
return criteria.List<Cat>();
What I'm finding is that both Cats and Tigers are returned by the query instead of just Cats. This makes sense, as a Tiger is a Cat. But in this particular case I only want Cats, and not the additional Tigers.
Does anyone know how I can achieve this?
This is actually a Feature. But i think you can do what you want by mixing "table per class hierarchy" with "table per subclass". Therefor you need a Discriminator Column on which you can perform the query on. This would look like the following:
<class name="Cat" table="Cat">
<id name="Id" type="Int64" column="ID">
<generator class="native"/>
</id>
<discriminator column="TYPE" type="string"/>
<subclass name="Tiger" discriminator-value="TIGER">
<join table="Tiger">
<property name="..." column="..."/>
</join>
</subclass>
</class>
After this you should be able to query on the discriminator-column like this:
session.CreateCriteria<Cat>()
.Add(Restrictions.IsNull("TYPE"))
.List<Cat>();