(fluent) nHibernate not deleting items from collection when the business object is constructed by a contract converter - nhibernate

I've been searching the answer to my questions for a few days and I've tried most of tips/answers and even looked through hundred of lines of logs generated by nHibernate, but I can't find a way to get my mapping working.. nHiberante still refuses to generate any delete statement when it's asked to SaveOrUpdate.
I have three separate entities, Person, House and Room... in the real word, each Person will have one house and each house can have multiple rooms, the mapping between house and room is one-to-many as expected, and I wanted a one-to-one mapping between Person and House then I read on the fluent nHibernate wiki that says it's really a many(Person)-to-one(house) because there's nothing at the database level that stops multiple persons sharing the same house. I guess that makes sense so I have my database schema and class map designed as below:
CREATE TABLE [dbo].[Person](
[PersonId] [int] IDENTITY(1,1) NOT NULL,
[HouseId] [int] NOT NULL
)
CREATE TABLE [dbo].[House](
[HouseId] [int] IDENTITY(1,1) NOT NULL,
[HouseName] [varchar](500) NOT NULL
)
CREATE TABLE [dbo].[Room](
[RoomId] [int] IDENTITY(1,1) NOT NULL,
[HouseId] [int] NOT NULL
)
HouseId on [Person] and [Room] are both foreign keys referencing HouseId from [House] table
public class PersonMap : ClassMap<Person>
{
public PersonMap()
{
Table("Person")
Id (x=>x.Id).Column("PersonId").GeneratedBy.Identity().UnsavedValue(0);
Reference(x=>x.House).Column("HouseId").ForeignKey("HouseId").Not.LazyLoad().Cascade.All();
}
}
public class HouseMap : HouseMap<Room>
{
public RoomMap()
{
Table("House")
Id (x=>x.Id).Column("HouseId").GeneratedBy.Identity().UnsavedValue(0);
HasMany(x => x.Persons)
.KeyColumn("HouseId")
.Not.LazyLoad()
.Cascade.AllDeleteOrphan().Inverse();
HasMany(x => x.Rooms)
.KeyColumn("HouseId")
.Not.LazyLoad()
.Cascade.AllDeleteOrphan().Inverse();
}
}
public class RoomMap : ClassMap<Room>
{
public RoomMap()
{
Table("Room")
Id (x=>x.Id).Column("RoomId").GeneratedBy.Identity().UnsavedValue(0);
Reference(x=>x.House).Column("HouseId").ForeignKey("HouseId").Not.LazyLoad().Cascade.All();
}
}
The class House holds two lists of type Person and Room respectively, and the class Person and Room both holds a reference back to the House object.
So I created an instance of House and set its two child lists Person(one 1) and Room(multiple rooms), and also link the properties of Person/Rooms back to House..and use session.SaveOrUpdate to save the Person it all works well.
Then I tried retrieve a instance of Person, and remove a room from Person.House.List and set room.House to null to fully break the reference, then use session.SaveOrUpdate to save the Person again (let's call it Person A), , nHibernate correctly generated delete statement to delete the orphan room that's not linked to its parent House any more.. so far so good...
Here is where my problem starts...in my application, I need to convert the object Person to a PersonContract and then sent it to the client, the client has the ability to remove a roomContract from PersonContract.HouseContract and send a copy of PersonContract back. Note in PersonContract, there're no bilateral reference, i.e. PersonContract has one property of HouseContract and HouseContract only contains a list of RoomContract so HouseContract does not keep a list of PersonContract and RoomContract doesn't have a HouseContract property.
Then on the server side, I'll convert the PersonContract back to a Person object and the converter is responsible for creating all new instances of Person,Room and houses and setting their Ids and build all bilateral references between them.
The problem is... when I then use session.SaveOrUpdate on the Person object (after contract conversion), nHibernate seems to ignore the fact a room has been removed from the collection but it does the update or insert job well..
I've carefully checked my contract converter to make sure all properties/references/Ids are properly set, and it looks exactly the same copy as the Person A above...but I don't understand why nHibernate is not doing the job here.
I've overridden both Equals and GetHashCode on Person/Room/House to return its id.
Another fact I observed is when I use session.SaveOrUpdate on the after-conversion Person object, nHibernate is generating lots of Update statements even though the value hasn't really changed, it looks as if nHibernate is not aware of the state of the persisted Person object so choose to update everything by identifying it using Id...
I'm quite new to nHibernate...so please let me know if I'm doing something horribly wrong here :)
Update: If I remove Inverse from one-to-many mapping between House and Room, then nHibernate will set the house ID of removed Room to NULL...better than not removing room at all but I really want it to remove orphaned rooms from DB..
Update 2: session.Merge on Person is working (SaveOrUpdate doesn't)....though I don't understand know why..

Try with wrapping your Save/Updates in a transaction:
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(SomeObject);
transaction.Commit();
}

Related

Invalid index N for this SqlParameterCollection with Count=N only when associated table has null record

I have a rather complex entity which will not save when a particular database table is missing a record. When the record exists the entity saves correctly. When the record does not I receive the exception:
Invalid index N for this SqlParameterCollection with Count=N
After reading a bunch of solutions found via Google and the most closely related questions on Stack Overflow:
What's causing “Invalid index nn for this SqlParameterCollection
with Count=nn” when a column is Null in the database?
Invalid Index n for this SqlParameterCollection with Count=n” OR “foreign key
cannot be null
I believe my issue has to do with the way I have my mapping files setup. The Customer entity has reference to the Person entity. Person maps to a table which we have read, but not write access to. It is when a record for the Person entity does not exist that I generate the exception. If the record exists no issue. I've set the reference to Person from customer to Nullable(). I have also double checked to ensure I do not have a property mapped twice from either entity.
Here is what I feel is the pertinent mapping information, but can provide more as needed:
Customer
//more mapping code...
References(x => x.Person, "snl_id").Nullable();
//more mapping code...
Person
//more mapping code...
ReadOnly();
Id(x => x.SnlId).Column("SNL_ID");
//more mapping code...
To further complicate matters we have some painful code to make NHibernate perform better when Person does not exist. I am not sure it applies here, but thought it pertinent enough to include in my question. We are using the below code because without it the NHibernate JIRA will create tons of queries. This solution is outlined in this Stack Overflow answer.
Customer's person property
public virtual Person Person
{
get
{
try
{
var snlId = per.Name;
return per;
}
catch
{
return null;
}
}
set
{
per = value;
}
}
private EPerson per;
What am I missing in my mappings that would cause this exception? Is there another piece of this problem that I am not seeing?
While Scott's solution of removing the snl_id property from the Customer class fixes the issue it causes problems that I cannot get around-- the snl_id can exist in the Customer table even there is not a corresponding Person table record. Since that is the case there are times when I will need access to the snl_id when I cannot get to it via the associated Person property.
I considered several alternative solutions but settled on creating a view of the Customer table including the Customer table primary key and the snl_id from the customer table. Then mapping that property via a join to the view.
Join("v_cust_id_snl_id", j => j.KeyColumn("cust_id").Map(x => x.SnlId, "snl_id")
This change allowed me to have my cake and eat it to. I was able to keep the SnlId property on customer, but no longer throw the exception when saving.
Do you have the snl_id referenced as a property in Customer as well as being the primary key for the child object? If so, this is causing the error you are receiving. Remove the property from Customer and use Person to get the value.

Fluent NHibernate: foreign key field not being set in unidirectional association

I have a database with a ProbateCases table and a Properties table. The Properties table has a foreign key to the ProbateCases table called ProbateCaseId, so the relationship between ProbateCases and Properties is one-to-many.
My domain layer has a ProbateCase class and a Property class. The ProbateCase class has a collection of Properties defined as follows:
private IList<Property> _properties = new List<Property>();
public virtual IEnumerable<Property> Properties { get { return _properties; } }
public virtual Property AddProperty()
{
Property property = new Property();
_properties.Add(property);
return property;
}
The corresponding part of the Fluent NHibernate mapping looks like this:
HasMany(x => x.Properties).Where("Deleted = 0").KeyColumn("ProbateCaseId").Cascade.All().Access.CamelCaseField(Prefix.Underscore);
Note that the association is unidirectional - the ProbateCase class has a collection of Properties, but the Property class does not have a ProbateCase member.
I'm finding that querying works fine - NHibernate is creating the appropriate SQL to get Properties with the appropriate ProbateCaseId value.
However, when I save a ProbateCase to which I have added a new Property, the INSERT SQL does NOT contain a value for the foreign key field - so I get a SQL Exception complaining of a NULL value in the foreign key:
INSERT INTO AdminOverview.Properties (PropertyName) VALUES ('Name of property') -- Where the hell is the ProbateCaseId field value???
Should I be expecting NHibernate to populate the foreign key value itself, or is there something else I should be doing?
From http://nhibernate.info/doc/nh/en/index.html#collections-onetomany:
Very Important Note: If the column of a association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse="true". See the discussion of bidirectional associations later in this chapter.

nhibernate - sproutcore : How to only retrieve reference ID's and not load the reference/relation?

I use as a front-end sproutcore, and as back-end an nhibernate driven openrasta REST solution.
In sproutcore, references are actualy ID's / guid's. So an Address entity in the Sproutcore model could be:
// sproutcore code
App.Address = App.Base.extend(
street: SC.Record.attr(String, { defaultValue: "" }),
houseNumber: SC.Record.attr(String),
city: SC.Record.toOne('Funda.City')
);
with test data:
Funda.Address.FIXTURES = [
{ guid: "1",
street: "MyHomeStreet",
houseNumber: "34",
city: "6"
}
]
Here you see that the reference city has a value of 6. When, at some point in your program, you want to use that reference, it is done by:
myAddress.Get("city").MyCityName
So, Sproutcore automatically uses the supplied ID in a REST Get, and retrieves the needed record. If the record is available in de local memory of the client (previously loaded), then no round trip is made to the server, otherwise a http get is done for that ID : "http://servername/city/6". Very nice.
Nhibernate (mapped using fluent-nhibernate):
public AddressMap()
{
Schema(Config.ConfigElement("nh_default_schema", "Funda"));
Not.LazyLoad();
//Cache.ReadWrite();
Id(x => x.guid).Unique().GeneratedBy.Identity();
Table("Address");
Map(x => x.street);
Map(x => x.houseNumber);
References(x => x.city,
"cityID").LazyLoad().ForeignKey("fk_Address_cityID_City_guid");
}
Here i specified the foreign key, and to map "cityID" on the database table. It works ok.
BUT (and these are my questions for the guru's):
You can specify to lazy load / eager load a reference (city). Off course you do not want to eager load all your references. SO generally your tied to lazy loading.
But when Openrast (or WCF or ...) serializes such an object, it iterates the properties, which causes all the get's of the properties to be fired, which causes all of the references to be lazy loaded.
SO if your entity has 5 references, 1 query for the base object, and 5 for the references will be done. You might better be off with eager loading then ....
This sucks... Or am i wrong?
As i showed how the model inside sproutcore works, i only want the ID's of the references. So i Don't want eagerloading, and also not lazy loading.
just a "Get * from Address where ID = %" and get that mapped to my Address entity.
THen i also have the ID's of the references which pleases Sproutcore and me (no loading of unneeded references). But.... can NHibernate map the ID's of the references only?
And can i later indicate nHibernate to fully load the reference?
One approach could be (but is not a nice one) to load all reference EAGER (with join) (what a waste of resources.. i know) and in my Sever-side Address entity:
// Note: NOT mapped as Datamember, is NOT serialized!
public virtual City city { get; set; }
Int32 _cityID;
[Datamember]
public virtual Int32 cityID
{
get
{
if (city != null)
return city .guid;
else
return _cityID;
}
set
{
if (city!= null && city.guid != value)
{
city= null;
_cityID = value;
}
else if (city == null)
{
_cityID = value;
}
}
}
So i get my ID property for Sproutcore, but on the downside all references are loaded.
A better idea for me???
nHibernate-to-linq
3a. I want to get my address without their references (but preferably with their id's)
Dao myDao = new Dao();
from p in myDao.All()
select p;
If cities are lazy loading in my mapping, how can i specify in the linq query that i want it also to include my city id only?
3b.
I want to get addresses with my cities loaded in 1 query: (which are mapped as lazyloaded)
Dao myDao = new Dao();
from p in myDao.All()
join p.city ???????
select p;
My Main Question:
As argued earlier, with lazy loading, all references are lazy loaded when serializing entities. How can I prevent this, and only get ID's of references in a more efficient way?
Thank you very much for reading, and hopefully you can help me and others with the same questions. Kind regards.
as a note you wrote you do this
myAddress.Get("city").MyCityName
when it should be
myAddress.get("city").get("MyCityName")
or
myAddress.getPath("city.MyCityName")
With that out of the way, I think your question is "How do I not load the city object until I want to?".
Assuming you are using datasources, you need to manage in your datasource when you request the city object. So in retrieveRecord in your datasource simply don't fire the request, and call dataSourceDidComplete with the appropriate arguments (look in the datasource.js file) so the city record is not in the BUSY state. You are basically telling the store the record was loaded, but you pass an empty hash, so the record has no data.
Of course the problem with this is at some point you will need to retrieve the record. You could define a global like App.WANTS_CITY and in retrieveRecords only do the retrieve when you want the city. You need to manage the value of that trigger; statecharts are a good place to do this.
Another part of your question was "How do I load a bunch of records at once, instead of one request for each record?"
Note on the datasource there is a method retrieveRecords. You can define your own implementation to this method, which would allow you to fetch any records you want -- that avoids N requests for N child records -- you can do them all in one request.
Finally, personally, I tend to write an API layer with methods like
getAddress
and
getCity
and invoke my API appropriately, when I actually want the objects. Part of this approach is I have a very light datasource -- I basically bail out of all the create/update/fetch methods depending on what my API layer handles. I use the pushRetrieve and related methods to update the store.
I do this because the store uses in datasources in a very rigid way. I like more flexibility; not all server APIs work in the same way.

Fluent NHibernate Mapping Single Column to Composite Key

I have a situation where i have defined an entity in my domain model in which I would like to expose a single id column.
public class OfferedProduct
{
public virtual string Id {get; set;}
//other properties
}
The legacy database table this will map to is
CREATE TABLE ProductGrouping
MemberNumber INT NOT NULL,
GroupId CHAR NOT NULL,
...
I dont want to compromise the domain model by introducing two properties and mapping them using the "CompositeId" construct.
CompositeId().KeyProperty(x => x.MemberNumber).KeyProperty(x => x.GroupId)
What I want ideally is to concatenate the two values in the form {MemberNumber}{GroupId} and expose this as the Id value. I would then use a Custom Type to handle how these values are concatenated when retrieved from the DB and broken apart when saving/selecting.
I have noticed that the "CompositeId" method does not allow a customType as with the standard "Id" call; but the "Id" method does not provide the ability to set multiple columns. I have seen examples where people have used "Map" to combine two columns using a custom type, but not for id values.
I have noticed the "CompositeId" has an overload that can take a custom identity class but I am unsure how to use it in this scenario.
CompositeId<OfferedProductIdentifier>(x => x.?)
Any help would be greatly appreciated.
in case someone comes here
CompositeId()
.KeyProperty(t => t.Id, c =>
c.Type(typeof(MyUserType)).ColumnName("MemberNumber").ColumnName("GroupId"));

NHibernate: Violated not-null constraint when saving HasMany relation with Cascade=AllDeleteOrphan

My bean looks like this:
public class A {
...
[HasMany (MapType = typeof(B), Table = "B_table", ColumnKey = "A_object_id",
Fetch = FetchEnum.Join,
RelationType = RelationType.List, Index = "id",
Cascade = ManyRelationCascadeEnum.AllDeleteOrphan)]
IList<B> BList { get; set; }
...
}
and when perform Save on this bean I expect that beans of type B will be automatically
saved (and deleted on update) too. NHibernate surely is trying that, but it does so
with B_table.A_object_id set to NULL first and then NHibernate updates B_table setting the proper B_table.A_object_id value (that is: A.ID).
This is not what I want, as I have a NOT NULL constraint in the database.
My question is: how to make NHibernate automatically save the child objects with the proper ID set from the start? I know I can create A bean, save it, get it's brand new ID, create B beans, set their A_object_id and then save B beans... but it's a workaround.
Unidirectional relationships (in which only the parent knows about the child) always result in an update for setting the Id. I'm not sure why and it doesn't make a lot of sense to me either but that's just how NHibernate works.
You need to create a bidirectional relationship where the HasMany would have an Inverse = true and B would have a reference to class A in it (which should be populated when you add B to the A collection.