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

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.

Related

NHibernate QueryOver.List causing N+1

Having a seemingly bizzare N+1 select problem in NHibernate. I am executing a query where I'm asking for a bunch of entities where one of its linked properties is null. I don't actually need the linked property to be returned in this case by NHibernate as its only for the purpose of selecting the right data.
First Entity is a booking Window
public class BookingWindow : Entity<BookingWindow>
{
// Blah blah blah
/// <summary>
/// Gets or sets the booking order item.
/// </summary>
/// <value>
/// The booking order item.
/// </value>
public virtual BookingWindowOrderItem BookingOrderItem { get; set; }
}
And the BookingWindowOrderItem as follows
public class BookingWindowOrderItem : OrderItem
{
// Blah blah blah
public virtual BookingWindow BookingWindow { get; set; }
}
Here are the respective mappings
public BookingWindowMap()
{
this.Schema("Customer");
this.Table("BookingWindows");
this.Id(x => x.Id).GeneratedBy.Guid();
this.Component(x => x.WindowPeriod, m =>
{
m.Map(x => x.Min, "StartTime");
m.Map(x => x.Max, "EndTime");
});
this.References(window => window.BookingOrderItem).PropertyRef("BookingWindow").Column("Id").LazyLoad().Nullable().ReadOnly();
this.Map(x => x.Price);
this.References(x => x.CustomerRoom).ForeignKey("RoomId").Column("RoomId");
}
And
public BookingWindowOrderItemMap()
{
this.DiscriminatorValue(1);
this.References(x => x.BookingWindow).LazyLoad().Column("OrderItemForeignId").ForeignKey("OrderItemForeignId");
}
Now When I execute the following query I get back the correct Booking windows that don't have an order item.
Session.QueryOver<BookingWindow>().Where(w => w.CustomerRoom.Id == Guid.Parse(roomId)).Left.JoinQueryOver(bw => bw.BookingOrderItem).WhereRestrictionOn(item => item.Id).IsNull.List<BookingWindow>();
So the first query gets issued to the database like so (the order item columns are selected which is a bit annoying but the real problem comes in a minute)
SELECT this_.Id as Id2_1_, this_.Price as Price2_1_, this_.RoomId as RoomId2_1_, this_.StartTime as StartTime2_1_, this_.EndTime as EndTime2_1_, bookingwin1_.Id as Id4_0_, bookingwin1_.Price as Price4_0_, bookingwin1_.Description as Descript4_4_0_, bookingwin1_.OrderId as OrderId4_0_, bookingwin1_.OrderItemParentId as OrderIte6_4_0_, bookingwin1_.OrderItemForeignId as OrderIte7_4_0_ FROM Customer.BookingWindows this_ left outer join Payment.OrderItem bookingwin1_ on this_.Id=bookingwin1_.OrderItemForeignId and bookingwin1_.OrderItemTypeId='1' WHERE this_.RoomId = ? and bookingwin1_.Id is null
But then for each booking window returned there is an extra select for the linked order item even though I haven't asked for it or need it. This happens within the query over method so I'm not doing any kind of iterating over the returned booking windows manually.
SELECT bookingwin0_.Id as Id4_0_, bookingwin0_.Price as Price4_0_, bookingwin0_.Description as Descript4_4_0_, bookingwin0_.OrderId as OrderId4_0_, bookingwin0_.OrderItemParentId as OrderIte6_4_0_, bookingwin0_.OrderItemForeignId as OrderIte7_4_0_ FROM Payment.OrderItem bookingwin0_ WHERE bookingwin0_.OrderItemForeignId=? and bookingwin0_.OrderItemTypeId='1'
Can anyone explain to me the error I have made here. Maybe its obvious but I've struggled for a few hours and at the end of my patience :)
I see one weird part in your mapping: Using References as a one-to-one mapping style. Maybe it is intended, but this is causing that issue you have.
Firstly, as documentation says [References / many-to-one][1]
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 /
one-to-many is the "other side" of the References relationship, and
gets applied on the "one side."
Other words, in the table of the BookingWindowOrderItemMap you store reference to BookingWindow. It could mean (by the DB Design), that there could be more records of OrderItem, referencing the same BookingWindow. But maybe this is what you want, and you check "uniqueness" elsewhere. The more I tried to understand your problem, I would vote for moving the reference to OrderItem in a column in the BookingWindow
Problem revealed:
To your issue. When NHibernate recieves the list of BookingWindow, the next step is to build a proxy. In this process, all valueType/string properties are set, and for references... And for references NHibernate tries to prepare the lazy load.
Simplified version is, that into each property BookingWindowOrderItem BookingOrderItem is injected a promise for an instance of the BookingWindowOrderItem, to be returned when firstly touched. In standard cases, when mapping References is used, NHibernate in this moment already loaded from the table of the BookingWindow the ReferenceId.
In your case, this ReferenceID is represented by virtual, readonly 'current item ID'. The ID which definetly exists... but the reference does not! We've selected only BookingWindows which has NULL instead of the reference.
But we do have NOT NULL Reference ID (representd by Current instance ID).
And we've used .Left.JoinQueryOver. So NHibernate is sure, that it already loaded all data in the first query... but is confused, because in his session is no OrderItem with the id equal to BookingWindow.ID/ReferenceId
That's the reason (why it tries to fix it... and does load it again)
So this is the answer, why NHibernate does "weird selects". Not a suggestion how to fix it ;) it could be another question and answer...

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

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();
}

Nhibernate foreign key constraint due to unexpected update of already inserted record

This is a strange one replicated in the following code:
using (ISession session = RepositoryTestHelper.SessionFactory.OpenSession())
{
//session.BeginTransaction();
UserRepo repo = new UserRepo(session);
CompanyRepo cRepo = new CompanyRepo(session);
var user = repo.FindByEmail("test.user#blah.com");
user.CompanyAssociations.Add(new CompanyUserAssoc()
{
User = user,
Company = cRepo.GetById(1)
});
repo.AddOrUpdate(user);
//session.Transaction.Commit();
}
And the relationship between user, company and CompanyUserAssoc is fairly straight forward:
For the company:
HasMany<CompanyUserAssoc>(x => x.UserAssociations).KeyColumn("User_id");
For the user:
HasMany<CompanyUserAssoc>(x => x.CompanyAssociations).KeyColumn("Company_id")
And for the association class itself:
References(x => x.Company).UniqueKey("CompanyId_UserId");
References(x => x.User).UniqueKey("CompanyId_UserId");
Now this is where I am baffled. Notice in my initial code that the begin and commit trans calls are commented out. This actually means the code will work! The CompanyUserAssoc is created and correctly references the user and the company with id of 1. Great!
But... sadly when I put this in a transaction i get this error:
{"The UPDATE statement conflicted with the FOREIGN KEY constraint \"FK3C47859753A62C6E\". The conflict occurred in database \"xxxx\", table \"dbo.Company\", column 'Id'.\r\nThe statement has been terminated."}
But why? Well that's my question. What i have see in the profiler is that it does this:
exec sp_executesql N'UPDATE [CompanyUserAssoc] SET Company_id = null WHERE Company_id = #p0',N'#p0 int',#p0=1
Wait... what? NULL? Why is it setting the company id to null? and why is it only doing this when in a transaction? What's "wrong" with my Nhibernate mapping?
Since both collections are mapped as non-inverse (the default) and the Company collection has no users, it will issue that update to clear the link table. Could you try setting the UserAssociations to inverse? Or, what I usually do when an explicit link table is involved, is set both HasManys to inverse and simply work with the link table directly.
This was a red herring in the end, a fish I have come to despise. I had not noticed in my haste and dependence on intellisense that the UserAssocations property of the Company was not an IList but an IList! yes I have stupidly chosen the Nhib map class instead of the domain class.. This weirdly didn't error in the way you would expect, and worked without a transaction, but why?
Well - Nhib was able to insert the assoc entry, but then believed it needed to update that same table with the id it had already inserted (because it somehow sees it as a different entity?). I would have expected a compilation error here in the map class itself as I was using the type, but no. And without a transaction the sql generated works.
I would investigate more as to why, but I've answered this just to highlight a rare and silly mistake which can lead to a lot of investigation if missed.

FluentHibernate: how to specify join-mapped table should never be inserted into

I have the following FluentNHibernate mapping:
public AssetMap()
{
Table("PRASSET");
Id(x => x.Id).Column("PRA_RECNUM").GeneratedBy.Sequence("PRA_RECNUM_GEN");
...fields mapped from PRASSET table
Join("PRSTOCK", m =>
{
m.Fetch.Join();
m.Optional();
m.KeyColumn("PRS_ASSRN");
...fields mapped from PRSTOCK table
});
}
As the m.Optional() call in the Join bit suggests, there may or may not be a corresponding PRSTOCK record.
The problem I am having is that when I save an Asset instance which is dirty only in respect of one of its PRASSET-mapped properties, and there isn't a corresponding PRSTOCK record, it's inserting one! This is emphatically not what I want to happen, since there should not be a corresponding PRSTOCK record in most cases.
Is there a tweak in the mapping configuration I can try? It would be enough at this stage just to prevent records ever being inserted into PRSTOCK, but ideally NHibernate should not try to update or insert PRSTOCK unless one of the Asset instance's properties mapped to PRSTOCK has been dirtied.
According to this blog post, NHibernate will always try to insert join-mapped records if the properties are not null. So I have made the appropriate properties nullable (default null) and hopefully it won't try to insert anymore.

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.