I have small problem with nHibernate (fluent) I have two objects, one contains another - a parent and a child (predefined objects, readonly).
mappings:
public class ParentClass
{
public virtual int Id { get; set; }
public virtual ChildClass Metoda { get; set; }
}
public ParentClassMap() {
Table("Wyceny");
Id(x => x.Id).Column("Id").GeneratedBy.TriggerIdentity();
References(x => x.Metoda).Column("RMW_ID");
}
public ChildClass
{
public virtual int Id { get; set; }
public virtual string Nazwa { get; set; }
}
public ChildClassMap()
{
Table("Metody");
Id(x => x.Id).Column("Id");
Map(x => x.Nazwa).Column("Nazwa_met");
}
Everything works perfectly until I chose child object with id = 0, reading still works for id=0, but when I'm trying to save or update Parent with correct ChildObject(readed previously from db through nHibernate), nHibernate stores null instead of value.
Any suggestions?
nHibernate 3.3.1.4000
fluent 1.4.0.0
The issue here would be the unsaved-value. NHibernate must decide if operations with object will be insert or update. This decision comes from unsaved-value setting, which is by default for int set to 0.
Try to extend your mapping of a ChildClass:
public ChildClassMap()
{
Table("Metody");
Id(x => x.Id)
.Column("Id")
.UnsavedValue(-1);
...
See 5.1.4. id, cite:
unsaved-value (optional - defaults to a "sensible" value): An identifier property value that indicates that an instance is newly instantiated (unsaved), distinguishing it from transient instances that were saved or loaded in a previous session.
And here is nice Id mapping overview by Adam Bar (the second half of the article)
Related
I would like to be able to add a collection of Notes to any of my main entities in my NHibernate application. I can see how you could do this with a seperate junction table per entity. However, I would like to be able to avoid this and only have one junction table - if this is possible.
Below is the code so far, however this will result in all Notes being loaded for every Entity and I only want to load the notes for that particular entity. What are the alternative approaches I need to take?
public class Entity
{
public virtual int Id { get; set; }
}
public class EntityType1 : Entity
{
public EntityType1()
{
Notes = new List<Note>();
}
public virtual string EntityTypeName { get; set; }
public virtual IList<Note> Notes {get;set;}
}
public class EntityType2 : Entity
{
public EntityType2()
{
Notes = new List<Note>();
}
public virtual string EntityType2Name { get; set; }
public virtual IList<Note> Notes { get; set; }
}
public class Note
{
public virtual int Id { get; set; }
public virtual IList<Entity> Entities { get; set; }
public virtual string NoteText { get; set; }
}
}
namespace FluentNHib.Mappings
{
public class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Id(m => m.Id);
}
}
public class EntityType1Map : ClassMap<EntityType1>
{
public EntityType1Map()
{
Id(m => m.Id);
Map(m => m.EntityTypeName1);
HasManyToMany(m => m.Notes).Table("EntityToNotes")
.ParentKeyColumn("EntityId")
.ChildKeyColumn("NoteId")
.LazyLoad()
.Cascade.SaveUpdate();
}
}
public class EntityType2Map : ClassMap<EntityType2>
{
public EntityType2Map()
{
Id(m => m.Id);
Map(m => m.EntityType2ame);
HasManyToMany(m => m.Notes).Table("EntityToNotes")
.ParentKeyColumn("EntityId")
.ChildKeyColumn("NoteId")
.LazyLoad()
.Cascade.SaveUpdate();
}
}
public class NoteMap : ClassMap<Note>
{
public NoteMap()
{
Id(m => m.Id);
Map(m => m.NoteText);
}
}
I am not sure what the real issue is:
...however this will result in all Notes being loaded for every Entity and I only want to load the notes for that particular entity...
Is the issue in lazy loading? or in fact that Entity1 and Entity2 can have same ID, therefore the references are mixed? (I expect that and this should be part of the answer below)
Anyhow, I would say that we can achieve what you need: map the Note with just one table EntityToNotes. And that is good.
But, in general, I would descourage you from using the many-to-many. It is just my own feeling, experience. Below are some links with more explanation:
Am I doing many to many incorrectly when using fluent nhibernate?
NHibernate how do you map a crossreference table to a bag?
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?
Draft of the SOLUTION:
So, firstly we have to extend the table "EntityToNotes" with two columns
EntityToNoteId column - we need a primary key for new pairing object
Discriminator column
The Discriminator column will be used for (almost like a standard inheritance)
inserting Discriminator value during creation
filtering te IList<Notes> per Entity
These could be the pairing Entity (with an abstract base gathering the common stuff)
public abstract class EntityToNote<TEntity>
{
public abstract string Discriminator { get; set; }
public virtual TEntity Entity {get;set;}
public virtual Note Note {get;set;}
}
// the pairing objects
public class EntityType1ToNote : EntityToNote<EntityType1>
{
string _discriminator = "EntityType1"; // here we set the discriminator
public virtual string Discriminator
{
get { return _discriminator; }
set { _discriminator = value; }
}
...
// Similar for other pairing objects
The Entities will now be referencing lists of pairing objects
public class EntityType1 : Entity
{
public virtual IList<EntityType1ToNote> Notes {get;set;}
...
public class EntityType2 : Entity
{
public virtual IList<EntityType2ToNote> Notes { get; set; }
...
Here is snippet of the mapping (all other Entities will have usual mapping, including ClassMaps for EntityType1ToNote, EntityType2ToNote...)
public class EntityType1Map : ClassMap<EntityType1>
{
public EntityType1Map()
{
Id(m => m.Id);
Map(m => m.EntityTypeName1);
HasMany(m => m.Notes)
// this "table" setting is redundant, it will come from EntityType1ToNote
//.Table("EntityToNotes")
.KeyColumn("EntityId")
// here is the trick, that only related rows will be selected
.Where("Discriminator = 'EntityType1'")
.Cascade.AllDeleteOrphan();
}
}
As I tried to explain in the links provided, we gained this way a lot. Mostly the ability to use more columns on the pairing table - e.g. Discriminator (later we can have more columns like SortBy...) and we are able to use powerful searching with subqueries - see Query on HasMany reference
Also, in fact, the pairing could be mapped via the real inheritance... But the main point here is: Instead of many-to-many we introduced the pairing object and gained a lot
I was using Fluent NHibernate and I had these two entities:
public class Location
{
public virtual int Id { get; protected set; }
public virtual string Name { get; set; }
private Stack<Place> _places;
public virtual IList<Place> Places
{
get { return _places.ToList(); }
set { _places = new Stack<Place>(value); }
}
public virtual void AddPlace(Place place)
{
_places.Push(place);
place.LocationReference = this;
}
}
public class Place
{
public virtual int Id { get; protected set; }
public virtual Location LocationReference { get; set; }
}
mapped as follow:
class LocationMap : ClassMap<Location>
{
public LocationMap()
{
Id(x => x.Id);
Map(x => x.Name)
.Not.Nullable()
.Unique();
HasMany(x => x.Places)
.Cascade.All()
.Inverse();
}
}
class PlaceMap : ClassMap<Place>
{
public PlaceMap()
{
Id(x => x.Id);
References(x => x.LocationReference);
}
}
I was using SQLite as RDBMS and I was saving only the Location, relying on Cascade() for managing Places insertion.
The two entities were successfully inserted in the DB, but when I tried to read a Location back and to access its list of Places, the list was empty.
After removing Inverse(), the code seemed to properly work. But when I inspected the DB, I found two columns within the Place table, while I expected only one of them: Location_id (that was empty) and LocationReference_id (that was set).
After one whole day of desperate googling, I noticed that everyone was naming the reference property as the class itself. So, I renamed it from LocationReference to Location, I added back the Inverse() call and everything worked fine. Only the column Location_id was in the DB, of course.
Does anyone know why this happened? Thanks.
I'm using fluent nhibernate to map a parent child relationship to the sql database.
Most of the times i let the parent entity save it's child entities,they are inserted in 1 transaction and if i'm not mistaken this wouldn't be possible if i used .Inverse() and sql identity columns.
The problem i have is that in 1 specific case i want to update the child entity and ONLY the child entity.
When i do this with my current setup the child record will lose the relationship to it's parent(if the parent object is null) or will replace my parent object completely(if i insert a dummy parent with just an id).
Does anyone know a way to achieve a single record update without affecting the foreign key?
I can think of a manual sql statement or a stored procedure but i'm hoping there is an nhibernate way.
I have the following setup (simplified for your convenience) :
public ProjectMap()
{
Table("Project");
Id(p=> p.Id);
HasMany(p => p.Risks).Not.LazyLoad();
}
public RiskMap()
{
Table("ProjectRisk");
Id(r=> r.Id);
References(r => r.Project).Column("ProjectId");
Map(r => r.Description);
}
public class Project
{
public virtual int Id { get; set; }
public virtual IList<Risk> Risks { get; set; }
}
public class Risk
{
public virtual int Id { get; set; }
public virtual string Description{ get; set; }
public virtual Project Project { get; set; }
}
As Miroslav suggested i'm using an HQL update statement now.
It's a little messy for my taste but it gets the job done.
Session.CreateQuery("update Risk set Closed = :completed where Id = :id")
.SetInt32("id", id)
.SetBoolean("completed", completed)
.ExecuteUpdate();
If I use session-per-transaction and call:
session.SaveOrUpdate(entity) corrected:
session.SaveOrUpdateCopy(entity)
..and entity is a transient instance with identity-Id=0. Shall the above line automatically update the Id of the entity, and make the instance persistent? Or should it do so on transaction.Commit? Or do I have to somehow code that explicitly?
Obviously the Id of the database row (new, since transient) is autogenerated and saved as some number, but I'm talking about the actual parameter instance here. Which is the business logic instance.
EDIT - Follow-up, of related problem.
Mappings:
public class StoreMap : ClassMap<Store>
{
public StoreMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Name);
HasMany(x => x.Staff) // 1:m
.Cascade.All();
HasManyToMany(x => x.Products) // m:m
.Cascade.All()
.Table("StoreProduct");
}
}
public class EmployeeMap : ClassMap<Employee>
{
public EmployeeMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.FirstName);
Map(x => x.LastName);
References(x => x.Store); // m:1
}
}
public class ProductMap : ClassMap<Product>
{
public ProductMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Name).Length(20);
Map(x => x.Price).CustomSqlType("decimal").Precision(9).Scale(2);
HasManyToMany(x => x.StoresStockedIn)
.Cascade.All()
.Inverse()
.Table("StoreProduct");
}
}
EDIT2
Class definitions:
public class Store
{
public int Id { get; private set; }
public string Name { get; set; }
public IList<Product> Products { get; set; }
public IList<Employee> Staff { get; set; }
public Store()
{
Products = new List<Product>();
Staff = new List<Employee>();
}
// AddProduct & AddEmployee is required. "NH needs you to set both sides before
// it will save correctly"
public void AddProduct(Product product)
{
product.StoresStockedIn.Add(this);
Products.Add(product);
}
public void AddEmployee(Employee employee)
{
employee.Store = this;
Staff.Add(employee);
}
}
public class Employee
{
public int Id { get; private set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Store Store { get; set; }
}
public class Product
{
public int Id { get; private set; }
public string Name { get; set; }
public decimal Price { get; set; }
public IList<Store> StoresStockedIn { get; private set; }
}
As far as your question is concerned, whenever you flush your session is when your entity is persisted to the database. When saving your (new) entity, NHibernate generates the ID for you using the generator you provided.
Keep in mind that an Identity generator is not recommended (see this post by Ayende).
When you use an Identity generator, your new entity is persisted to the database when you save, even if you don't flush to the database. The reason this happens is because NHibernate needs to provide you with an ID for the entity, which it can't do without doing a roundtrip to the database.
A better solution would be to use something like a Guid generator, or HiLo if you want 'normal' values. This way you can save your entity without actually having to do a database roundtrip, which allows you to do a lot more performance wise (batching comes to mind).
I'm not sure I understand your question. The actual saving to the database occurs when the session is flushed (e.g. by committing the transaction). Calling SaveOrUpdate() doesn't itself save the entity, it just informs the session that the entity is due to be saved when the session is flushed.
Assuming that the ID of the entity maps to an identity field in the database and that your mapping tells NHibernate that identity is set by the database, then the ID set by the database will be set as the entity's ID when it is saved.
Nhibernate will set the ID property of your entity just after SaveOrUpdate call.
I noticed I saved by calling:
session.SaveOrUpdateCopy(entity);
..which does NOT update the Id. But by changing to:
session.SaveOrUpdate(entity);
..the Id's of transient entity will be updated.
I probably misunderstood the documentation (?).. Section 9.4.2 says:
SaveOrUpdateCopy(Object o)... If the given instance is unsaved or does not exist in the database, NHibernate will save it and return it as a newly persistent instance.
Is it just me, or does it not sound like a transient object (unsaved), will be "returned as persistent" ? Doesn't that mean with updated Id? Would appreciate a clarification how to interpret this sentence correctly (?)
Question: is there a way to map a single foreign key to a number of mutually exclusive tables, based on a context?
Background...
In my specific example, I have the following domain graph, representing an insurance claim which can be against a vehicle or property:
public enum InvolvedPartyContext
{
Vehicle = 1,
Property = 2
}
public class Claim
{
public virtual Guid Id { get; set; }
public virtual InvolvedPartyContext InvolvedPartyContext { get; set; }
public virtual Vehicle Vehicle { get; set; } // set if Context = Vehicle
public virtual Property Property { get; set; } // set if Context = Property
}
public class Vehicle { //... }
public class Property { //... }
The SQL looks like this (notice the single foreign key InvolvedPartyId):
CREATE TABLE Claims (
Id uniqueidentifier NOT NULL,
InvolvedPartyContext int NOT NULL,
InvolvedPartyId uniqueidentifier NOT NULL
)
CREATE TABLE Vehicles (
Id uniqueidentifier NOT NULL,
Registration varchar(20) NOT NULL
)
CREATE TABLE Properties (
Id uniqueidentifier NOT NULL,
PostCode varchar(20) NOT NULL
)
The Fluent NHibernate mapping file for Claim:
public ClaimMap()
{
Id(x => x.Id);
Map(x => x.InvolvedPartyContext).CustomTypeIs(typeof(InvolvedPartyContext));
References(x => x.Vehicle, "InvolvedPartyId");
References(x => x.Property, "InvolvedPartyId");
}
This throws an "Invalid index {n} for this SqlParameterCollection with Count {m}" exception, since the same field (InvolvedPartyId) is mapped twice. A simple fix would be to create VehicleId and PropertyId fields, but in the real world there are many more contexts, so this isn't very flexible.
Personally, I wouldn't go with the design you have. Instead I'd create subclasses of your Claim class, VehicleClaim and PropertyClaim respectively.
public class VehicleClaim : Claim
{
public virtual Vehicle Vehicle { get; set; }
}
Then change your mappings to use your InvolvedPartyContext column as a discriminator (the column which NHibernate uses to determine which class the row represents), and create subclass mappings for each subclass.
public class ClaimMap : ClassMap<Claim>
{
public ClaimMap()
{
Id(x => x.Id);
DiscriminateSubClassesOnColumn("InvolvedPartyContext");
}
}
public class VehicleClaimMap : SubclassMap<VehicleClaim>
{
public VehicleClaimMap()
{
DiscriminatorValue(1);
References(x => x.Vehicle);
}
}
If you really do want to run with what you've got, you should look into the any mappings; there isn't a lot of documentation on them, but you use the ReferencesAny method.