Fluent NHibernate: How to create one-to-one bidirectional mapping? - nhibernate

I had a similar question to Fluent NHibernate: How to create one-to-many bidirectional mapping? but I was interested in the situation when I have a one-to-one mapping. For instance
Umbrealla
ID
Owner
UmbreallaOwner
ID
Umbrella
As we know each umbrella can only be owned by one person and nobody owns more than one umbrella. In a fluent map I would have something like
UmbrellaMap()
{
Id(x=>x.ID);
References<UmbrellaOwner>(x=>x.Owner);
}
UmbrellaOwnerMap()
{
Id(x=>x.ID);
References<Umbrella>(x=>x.Umbrella);
}
When creating the tables fluent will create a field in umbrella referncing the ID of umbrellaOwner and a field in umbrellaOwner referencing umbrella. Is there any way to change the mapping such that only one foreign key will be created but the Umbrella property and the Owner property will both exist? The examples I have seen involve setting the relations up in both directions so adding a new Umbrella looks like
AddUmbrealla(UmbrellaOwner owner)
{
var brolly = new Umbrella();
brolly.Owner = owner;
owner.Umbrella = brolly;
session.Save(owner); //assume cascade
}
which seems logical but a bit cumbersome.

Well, a reference is a reference; one object has a reference to the other. The reverse is not necessarily true.
In your case, you MIGHT get away with a HasOne relationship. However, HasOne is normally for denormalized data. Say you wanted more info about the owner, but you could not change Owner's schema because other code depended on it. You'd create an AdditionalOwnerInfo object, and create a table in the schema in which the OwnerID field of the table was a foreign key to Owner, and also the primary key of the table.
Ayende recommends a two-sided References() relationship in 99.9% of one-to-one cases, where the second object is conceptually separate from the first, but there is an implicit "I alone own exactly one thing" type of relationship. You can enforce the "one and one only" nature of the reference using a Unique().Not.Nullable() modifier set on the References mapping.
To streamline the referential setup, consider defining one object (UmbrellaOwner) as the "parent" and the other (Umbrella) as the "child", and in the parent's property setter, set the child's parent to the current reference:
public class Umbrella
{
public virtual string ID { get; set; }
public virtual Owner Owner { get; set; }
}
public class UmbrellaOwner
{
public virtual string ID { get; set; }
private Umbrella umbrella;
public virtual Umbrella Umbrella
{
get{
return umbrella;
}
set{
umbrella = value;
if(umbrella != null) umbrella.Owner = this;
}
}
}
Now, when you assign the child to the parent, the backreference is automagically set up:
var owner = new UmbrellaOwner{Umbrella = new Umbrella()};
Assert.AreEqual(owner, owner.Umbrella.Owner); //true;

Related

One controller/view for entities with same properties? (DRY code)

We have quite a few lookup entities that all have the same structure - just ID and Name. For example, Gender, Ethnicity are just dropdown lists on a Patient view. Each lookup entity has views for viewing/adding/editing the values. Each entity has its own controller with nearly identical actions - Index (view list), Create, and Edit. The only thing different is the type.
Is there a way to create one controller and one set of views to manage all of these lookup entities using generics, a base entity, some other technique or a combination of these?
One simple thing you can do is create an Interface for ID and Name. Then inherit it in the models that you need to.
Example
public interface IEntity
{
int Id { get; set; }
string Name { get; set; }
}
Inherit this in your models
public class Gender : IEntity
{
}

do i need to set the value of the ONE side when adding an entity to a one-to-many collection?

This seems like a super obvious question, but I haven't been able to find a clear answer.
I'm using FluentNHibernate automapping with the DefaultCascade.All() convention.
Entities are saving, but in one-to-many relationships I'm having to provide the one side on my many side even though i'm saving by adding to a collection.
An example will probably explain this better:
Lets say I've got these two classes:
public class Owner
{
public virtual IList<PetDog> Dogs { get; set; }
}
public class PetDog
{
public virtual Owner Owner { get; set; }
}
In order to add a new PetDog to the Dogs collection on an owner, I feel like I should be able to call
Owner.Dogs.Add(new PetDog());
and dispose my ISession. However, I'm just getting the Owner saving and thats it.
If I explicitly set
Owner.Dogs.Add(new PetDog { Owner = Owner })
It works.
Is there a way to avoid explicitly providing that value?
This can be done by marking the Owner class as the owner of the relationship by setting inverse = false in the Owner mapping, i.e.
HasMany(x => x.Dogs)
.Not.Inverse()
.Cascade.AllDeleteOrphan();
Then the owner_id foreign key in the PetDog table will be populated on commit, i.e.
using (var transaction = session.BeginTransaction())
{
var order = new Owner() {Dogs = new List<PetDog>()};
order.Dogs.Add(new PetDog() );
order.Dogs.Add(new PetDog() );
session.Save(order);
transaction.Commit();
}
Alternatively, instead of using transaction you can call session.Flush() instead which will cause the new data to be inserted into the DB, i.e.
var order = new Owner() {Dogs = new List<PetDog>()};
order.Dogs.Add(new PetDog() );
order.Dogs.Add(new PetDog() );
session.Save(order);
session.Flush(); // data persisted to DBMS here.
Please note that the use of session.Flush() is not recommended best practice. It is recommended that explicit transactions are used. Please see this blog post by Ayende Rahien for further details.

Fluent nHibernate Auto Mapping - Issue with AutoMapping Override

I've just tried to get a project up and running with Fluent Automapping (I'm familiar with Fluent but used to write each of the maps)
I have an object ScriptType which has a ParseRules property
public class ScriptType : EntityBase
{
public virtual string Name { get; set; }
public virtual IList<ParseRule> ParseRules { get; set; }
}
This is being Auto Mapped as HasMany and I wanted References.
I therefore added an AutoMapping override to another assembly ...
public class ScriptTypeOverride : IAutoMappingOverride<ScriptType>
{
public void Override(AutoMapping<ScriptType> mapping)
{
mapping.References(x => x.ParseRules);
}
}
And altered my configuration as so ...
return configuration
.Mappings(m => m.AutoMappings
.Add(AutoMap.AssemblyOf<DatabaseInfo>()
.IgnoreBase<EntityBase>()
.Conventions.AddFromAssemblyOf<KeyConvention>()
.UseOverridesFromAssemblyOf<ScriptTypeOverride>()));
But I get this .... :(
An association from the table ScriptType refers to an unmapped class: System.Collections.Generic.IList`1[[GIT.ScriptWizard.Entities.ParseRule ...
Can anyone help please?
References is for creating many-to-one
relationships between two entities,
and is applied on the "many side."
You're referencing a single other
entity, so you use the References
method. HasMany is the "other side" of
the References relationship, and gets
applied on the "one side."
From Fluent's website.
How should your relation work? It looks like a classic one ScriptType-to-many ParseRules, so this should be HasMany on ScriptType's side, as Fluent does.
Maybe, if you want to have bidirectional relationship here, where ParseRule's side is the "owning" side of the relation, you should use Inverse() in ScriptType.ParseRules mapping override.

Do I have to implement Add/Delete methods in my NHibernate entities?

This is a sample from the Fluent NHibernate website:
Compared to the Entitiy Framework I have ADD methods in my POCO in this code sample using NHibernate. With the EF I did context.Add or context.AddObject etc... the context had the methods to put one entity into the others entity collection!
Do I really have to implement Add/Delete/Update methods (I do not mean the real database CRUD operations!) in a NHibernate entity ?
public class Store
{
public virtual int Id { get; private set; }
public virtual string Name { get; set; }
public virtual IList<Product> Products { get; set; }
public virtual IList<Employee> Staff { get; set; }
public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}
public virtual void AddProduct(Product product)
{
product.StoresStockedIn.Add(this);
Products.Add(product);
}
public virtual void AddEmployee(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
}
You don't have to do this for nhibernate, you have to do this for keep in-memory consistence and not repeat yourself.
Consistence in memory
If you have a two way relationship, lets say Order has Lines, and Line as a relationship to order. You don't want to have a reference from one side and not from the other.
If you just do:
order.Lines.Add(line);
You have made a reference from Order to Line, but Line.Order property remains null. So your in-memory instances are not consistent.
Don't Repeat Yourself
You can use the following code :
order.Lines.Add(line);
line.Order = order;
but you will be repeating yourself, so it is better to put this code in only one place, and the best place is as order.AddLine(..).
You don't have to. You could just call SomeStore.Products.Add(someProduct) directly from outside of your entity. But it's often good practice to make the collections 'read-only' from a public perspective, and using an add method in the entity for adding items.
One benefit of this is that you can put additional logic in there. For instance in your store example, you could set a 'storesStockedIn' collection (if there was such a thing) in the same method, and so keep all the logic about to creating that relationship in one place.
This isn't really a NHibernate thing, but rather an OOP thing. (Although I'm not familiar with EF - maybe it automates some of this for you). The design decisions are exactly the same as if it was just an unpersisted poco (without NHibernate).

NHibernate one way, one-to-many, mapping question

I have a scenario in NHibernate where I have a one-to-many relationship between entities Employee and EmployeeStatus.
Employee has properties eg: ID, Name and an IList of EmployeeStatus, whilst EmployeeStatus, for the purposes of this question, just has it's own ID and some free text.
I don't need to hold a reference to Employee from EmployeeStatus, the management of status' will be done purely through the Employee entity - adding to the IList property. IE: I want to quite simply be able to do the following;
Employee e = new Employee();
e.Name = "Tony";
e.StatusList.Add( new EmployeeStatus("Status A") );
e.StatusList.Add( new EmployeeStatus("Status B") );
session.Save(e);
I've tried various methods, including creating a one way one-to-many mapping where inverse is false, cascade set to all-delete-orphan, which all looks like it should work, but it generates an exception about being unable to set the EmployeeId in EmployeeStatus. I'm led to believe that this is because NHibernate wants to do an insert with EmployeeId as NULL and then update it to the ID of the parent.
I guess I'm missing something here, so quite simply - can anyone tell me what my mapping file should look like to achieve the above?
Thanks in advance
Tony
-- edit: Heres a rough idea of the classes as requested --
public class Employee
{
private IList<EmployeeStatus> _statusList;
public Employee()
{
_statusList = new List<EmployeeStatus>();
}
public virtual int Id{ get; set; }
public virtual string Name{ get; set; }
public virtual IList<EmployeeStatus> StatusList
{
get
{
return _statusList;
}
}
}
public class EmployeeStatus
{
public virtual int Id{ get; set; }
public virtual string StatusText{ get; set; }
public EmployeeStatus()
{
}
public EmployeeStatus(string statusText)
{
StatusText = statusText;
}
}
The scenario you've described is just a basic one-to-many mapping. Here is the Fluent NHibernate mapping for this:
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
WithTable("Employee");
HasMany(employee => employee.StatusList)
.Cascade.All();
}
}
You do not need to maintain a reference from EmployeeStatus back to Employee to achieve this.
Turns out that what I want to do isn't possible - you have to have a bi-directional association, and must set the child's parent reference. Not a massive problem I suppose, but didn't want to hold references in the child that I don't need within my code directly.
I may not of explained clearly, but an employee status cannot be linked to more than one employee. It's definitely 1 (employee) to many (status')
In the physical database, the status entity has an employeeID field, which isn't in the domain - IE: I hold no reference back to employee from the status entity, but the physical field should be inferred from the owner of the collection - In fact, it does do this if I set the EmployeeID field in the status table to nullable - it actually executes 2 SQL statements - an insert and then an update, the EmployeeID being set in the update.
Thanks,
Tony
Can you post the code for the classes?
Are you trying to keep a history of statuses for an Employee?
-- Edit --
Looks like you are going to need many-to-many, since the child in the relationship (EmployeeStatus) has no reference back to the parent (Employee).
-- Edit 2 --
If you want the insert to be done as 1 call to the DB, you are going to need to add an Employee property to the EmployeeStatus class, and set the Inverse=true. And I'm pretty sure that you are going to need to add some logic which sets the bi-directional relationship in the objects. I.E.
public void AddStatus(EmployeeStatus status)
{
this.StatusList.Add(status);
status.Employee = this;
}