NHibernate: mapping bidirectional one-to-many with IList semantics - nhibernate

Here are the relevant pieces. This is a parent object:
public class Article
{
public virtual IList<ArticleRevision> Revisions { get; set; }
}
<list name="Revisions" cascade="all" inverse="true" table="ArticleRevision">
<cache usage="read-write" />
<key column="ArticleID" not-null="true" />
<index column="Number" type="int32" />
<one-to-many class="ArticleRevision" />
</list>
This is a child:
public class ArticleRevision
{
public virtual Article Article { get; set; }
}
<many-to-one name="Article" column="ArticleID" not-null="true" />
Now, I create an instance of Article, add one ArticleRevision to Article.Revisions collection, set ArticleRevision.Article to reference the Article instance and shove it to the database:
INSERT
INTO
ArticleRevision
(Content, Keywords, CreatedAt, SiteID, ArticleID, CreatedByUserID, ID)
VALUES
(#p0, #p1, #p2, #p3, #p4, #p5, #p6);
No Number column gets inserted.
How do I correctly map a bidirectional one-to-many collection with list semantics in NHibernate?

From the NHibernate documentation:
Please note that NHibernate does not
support bidirectional one-to-many
associations with an indexed
collection (list, map or array) as the
"many" end, you have to use a set or
bag mapping.

As far as I know, All of ORM frameworks state that when you have a bi-directional relationship, you should define one side, in which your ORM will rely on when updating the state of your bi-directional relationship. Otherwise, you can get side effects such as
Inconsistent mapping
Violation of constraints
Among other stuffs
In your case, the column ArticleID can be managed by both ends of the relationship. Because of that, you should mark one side as inverse="true", which should be placed in the many side when you have a #OneToMany relationship.
However, you can achieve the same behavior by disabling the property ArticleRevision.Article, by setting its mapping as
<many-to-one name="Article" column="ArticleID" insert="false" update="false" />
Using insert="false" update="false", you are saying: NHibernate, does not care about me, in which, as a result, NHibenate you simply ignore it. Now, discard inverse="false" in the property Article.Revisions, and you will get your answer.
UPDATE:
Remove not-null="true" from your key column because it is updated right after ArticleRevision. As a consequence, you will get a constraint violation otherwise.

Related

NHibernate how do you map a crossreference table to a bag?

I have recently inherited a project at work that contains NHibernate. I am extremely new to it and have to make a modification to one of the mappings. I've read through the documentation here and I'm still not sure how to do this or if my understanding/terminology is even correct.
So given the following table structure I need a bag that will get me the ProjectName:
User
UserID (PK)
ProjectUser
UserID (PK, FK User.UserID)
ProjectID (PK, FK Project.ProjectID)
Project
ProjectID (PK)
ProjectName
Here is the existing bag mapping and it correctly returns the ProjectID, but now I'm trying to understand how I need to modify it to return both the ProjectID and the ProjectName:
<bag name="Projects" table="ProjectUser" lazy="true" inverse="true" cascade="save-update">
<key column="UserId"></key>
<many-to-many class="Project" column="ProjectID"></many-to-many>
</bag>
Well, your mapping seems to be correct, i.e. already returning the ProjectName. To be sure please, check that the object Project is mapped like this:
<class name="Project" table="Project">
<id name="Id" column="ProjectID" generator class="native"/>
<!-- above as the Id we have mapping for column ProjectId
below the C# Name will contain the column ProjectName -->
<property name="Name" column="ProjectName" />
<!-- related Users to this Project -->
<bag name="Users" table="ProjectUser" lazy="true" >
<key column="ProjectID"></key>
<many-to-many class="User" column="UserID" />
</bag>
</class>
And the Project would be like this
public class Project
{
public virtual int Id { get; set;}
public virtual string Name { get; set;}
public virtual IList<User> Users { get; set;}
...
So, having this in place, we should be able to use the User:
public class User
{
public virtual IList<Project> Projects { get; set;}
...
mapped and loaded by NHibernate like this
user = session.Get<User>(x) // id of searched user
foreach(var project in user.Projects)
{
var name = project.Name;
var id = project.Id;
...
}
NOTES:
In case of many-to-many there obviously could/should be <bag> mapping on both sides - Project and User. But only one of them can have inverse="true". So in this case Project.Users does not have that.
The cascade setting on many-to-many is doing (most likely) different thing than one would expect. It is not related to the pairing table but to the second end of that mapping.
Cascading of the pairing object is done out of the box. It does not have to be mapped, in fact it cannot be mapped or turned off... Other words I would suggest to remove that cascade, unless you really want to change the Project in persistence, if you are working with some User.
Check also:
23.2. Author/Work
How to do it without many-to-many ... with explicit pairing object as a mapped Entity: Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships? or Am I doing many to many incorrectly when using fluent nhibernate?

Many-to-many NHibernate mapping to a legacy app view

My many-to-many relationship does not involve the standard "joining-table" approach, in which a table stores the "FK1-to-FK2" relationships.
Instead, I'm "loosely" joining to a legacy read-only view as follows:
Appointment class (based on the Appointment table)
int AppointmentId (PK)
int OrderId
List<LegacyOrder> LegacyOrders
LegacyOrder class (based on the LEGACY_ORDERS_VIEW view in our legacy system)
int OrderId (composite PK)
int VersionNumber (composite PK)
An Appointment can have many (versions of a) LegacyOrder.
A LegacyOrder can have many Appointments, but this relationship is not important in our application.
I want to populate the LegacyOrders property with all LegacyOrders for the specified OrderId.
My attempt at mapping is as follows:
<class name="Appointment" table="Appointments" lazy="true">
<bag name="Orders" table="LEGACY_ORDERS_VIEW" inverse="true">
<key column="OrderId" />
<many-to-many class="LegacyOrder" column="ORDER_ID" />
</bag>
</class>
....but I'm getting "could not execute query" exceptions due to invalid SQL.
I think the table referred to in the <bag> mapping should be the "joining table".... but I don't have one.
I'm fairly sure my mapping approach is fundamentally wrong.... what's the right way to go about it?
Edit:
Thanks Radim: perhaps a better name for LegacyOrder would be LegacyOrderVersion: each record in that view corresponds to a "version" of an order, rather than an order.
i.e. An order may be for 100 units, then when say 20 units are collected, another record is written with the same OrderId but for 80 units. (I did warn you it was legacy :)
If an Appointment (in the new system) can retrieve all related LegacyOrderVersions, then it can derive useful properties such as CurrentLegacyOrderVersion and OriginalLegacyOrderVersion.
FWIW: this works great for me:
<class name="Appointment" table="Appointments" lazy="true">
<bag name="Orders" inverse="true">
<key property-ref="OrderId" column="ORDER_ID" />
<one-to-many class="LegacyOrder" />
</bag>
</class>
One way how to solve this a bit challenging DB structure, could be with the property-ref feature. See more details here: 5.1.10. many-to-one, working even for our many-to-many scenario.
So firstly we have to map the property, which we will use as a reference:
<class name="Appointment" table="Appointments" lazy="true">
...
// the column name is coming from the Appointment table
<property name="OrderId" column="ORDER_ID" />
So, now we have mapped the OrderId - the property (column) - which we will use to map the <bag>.
Well, honestly, now I am not sure what your thoughts were. In case that LegacyOrder would have one column mapped as key (the Order_ID) we can do it like this.
<bag name="Orders" table="LEGACY_ORDERS_VIEW" inverse="true">
<key column="ORDER_ID" property-ref="OrderId" />
<many-to-many class="LegacyOrder" formula="ORDER_ID" />
</bag>
But that's not reasonable, because the Order_Id is not unique. In fact the LegacyOrder view, does not seem to be the entity at all. It could be some real intermediate structure.
I would say, that what the pairing view Legacy_orders_view represents, is the map (dictionary) saying: The Order with ID == X, had these Versions.
This information, the int Version numbers, is the only thing/information I can find out as really interesting. The OrderId is representing still the same Order
Anyhow, with the proeprty-ref and more detailed knowledge what you need to achieve we can at the end have:
// I. Map
public virtual IDictionary<int, Order> OrderMap { get; set; }
above the Version will play the role of the Key, the Order is questinable, because it will be the same Order as the OrderId says
// II. Version collection
public virtual IList<int> OrderVersions { get; set; }
in this case we will get set of int numbers related to the OrderId. Seems to be the only interesting message we can get.
III. There must be more information, about your entity/DB model. Why does the Legacy_orders_view exists at all? What would we like to get from that "relation" at the end?

Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?

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!

How to map Nhibernate collection

I have customer and BankAccount tables. CustomerID is a foreign key in the BankAccount table. Here we have to do with one to many relationship. I have done mapping for CustomerInfo and BankAccountInfo. I have attached with the question post.
Here we want to do with collection using Iesi.collections. Does anyone have ideas on how to do this in Nhibernate?
To do Collection mapping start reading this documentation: Chapter 6. Collection Mapping
In case that you will use the Iesi collections, most suitable could be ISet<>, which is designed to allow only unqiue values to be inserted:
public class Customer
{
public virtual Iesi.Collections.Generic.ISet<BankAccount> BankAccounts { get; set; }
}
And the mapping could be like this
<set name="BankAccounts" inverse="true" lazy="true" table="BankAccount"
cascade="all" batch-size="25" >
<key column="CustomerId" />
<one-to-many class="BankAccount" />
</set>
Notes: This mapping has some redundant attributes, which can help to do smarter loading. E.g. batch-size will avoid 1+N loads, while doing 1 + N/25... cascade will implicitly solve the persistence of added items... read more in doc.
The complete ISet description: http://ayende.com/blog/3943/nhibernate-mapping-set

Strategies for Mapping Views in NHibernate

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.