Nhibernate - Cascading inverse doesn't get saved - nhibernate

I am building a small web application and using nhibernate as my DAL.
I have the following situation:
Father class - Profile Class:
{
long ID
string Name
[A whole bunch of info]
List<Voting> Votes
}
Son class - Votes Class:
{
long id
short rating
}
This is my mapping:
HasMany(o => o.Votes)
.LazyLoad()
.AsBag()
.Inverse()
.Cascade.All();
I want the following to happen:
Users will be able to rate a profile, and then all I need to do is merely call update on the profile itself, instead of sending a Save call on each Vote.
This i what I am currently doing:
var myProfile = new Profile();
myProfile.Rate(1,2);
myProfile.Rate(5,2);
myProfile.Rate(20,2);
Session.SaveOrUpdate(myProfile);
Sadly enough nothing gets saved. Why?
Thank you!

If your one-to-many is mapped as inverse, the other side needs to know the parent object. So your Votes class needs a reference to a Profile (mapped as many-to-one).
You also have to make sure that this reference to the parent object is set in your code.
Additional note: Your code sample doesn't make much sense. If you create a new profile, you have to call Session.Save and not Session.Update.

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...

NHibernate ReferencesAny pulling back the wrong type

I've got a table called AdministratorPrivilages that has the following fields:
ID
List item
MemberId
Value
MemberType
Now, the members can be of two types (Enterprise and Express). Enterprise members live in the enterprise table. Express members live in the expressmember table. I've tried to do my fluent mapping like so.
public class AdministratorPrivilegesMapping : ClassMap<AdministratorPrivileges>
{
public AdministratorPrivilegesMapping()
{
Id(x=>x.Id);
Map(x => x.Value).Column("Value");
ReferencesAny(x => x.Member)
.EntityTypeColumn("MemberType")
.EntityIdentifierColumn("MemberId")
.IdentityType<Int32>()
.AddMetaValue<ExpressMember>("Express")
.AddMetaValue<Member>("Enterprise");
}
}
Both member tables have integer ids with ascending values. When I try to pull back the privilages associated with enterprise member 10, I'm getting the permission set associated with Express Member 10. Both other tables are mapped with the old school hbm mapping files.
Am I missing something obvious? I'm using NHibernate 2.1 and FluentNhibernate 1.1
I actually found the solution using .Where().
My situation is a little different, I have objectA and objectB.
objectA contains objectB, but objectB also contains a collection of objectBs.
"objectA":{
"someProp" : "(string)",
"objectB":{
"someProp" : "(string)",
"comeCol" : [
"(objectB)"
]
}
}
So the "Parent" property of objectB can either be of type objectB or objectA, which is why I needed to use ReferencesAny in the mapping of objectB.
Mapping looks like
ReferencesAny( x => x.Parent )
.IdentityType< int >()
.MetaType< string >()
.EntityTypeColumn( "ParentType" )
.EntityIdentifierColumn( "ParentId" )
.AddMetaValue< objectA >( "E" )
.AddMetaValue< objectB >( "S" )
.Access.Property()
.LazyLoad()
.Cascade.All();
All this works well when saving, however, my problem occured when retrieving, because the framework wasn't told what to retrieve and simply retrieved everything.
So now here is the mapping of the collection that fixed the problem:
HasMany( x => x.objectBs )
.KeyColumn( "ParentId" )
.Where( "ParentType = 'S'" )
.Cascade.All()
.LazyLoad();
Hope it will help anyone in the same situation :)
As seems to always be the case when I post on Stackoverflow, I'm being a complete goober and missing something glaringly obvious that cleared itself up with a good night's rest and some caffeine. I needed to look into the mappings for the ExpressMember and Member classes. Turns out the bag declarations there didn't filter by the type of object appropriately. Initially, I thought I had made the breaking change, when in fact it was actually a very old issue. The collision in id numbers between the two different types of members was not an issue for a very long time, as express members typically have either all the permissions or none, as do most of the older members (which were created first under an admin/plebe scheme with privileges being broken out later).

I get an error in my first (fluent) nhibernate query Initializing[type]-failed... no session or session was closed

I just started with NHibernate, created my mappings using fluent NHibernate as follows:
public class CustomerMap : ClassMap<Customer>
{
public CustomerMap()
{
Id(x => x._id, "Id");
Map(x => x._KdNr, "KdNr");
Map(x => x._Name, "Name");
HasMany(x => x._Contact)
.Table("Contacts")
.KeyColumn("FKCustomerID")
.LazyLoad();
}
}
public class ContactMap : ClassMap<Contact>
{
public ContactMap()
{
Id(x => x._id, "Id");
Map(x => x._Name, "Name");
}
}
And to save new records works also:
public static void AddCustomer(Customer cust)
{
using (var session = SessionFactory.Instance.OpenSession())
{
session.Save(cust);
session.Save(cust._Contact);
session.Flush();
}
}
Then I tried to select the customer I added using:
using (var session = SessionFactory.Instance.OpenSession())
{
try
{
var v = session.CreateQuery("from Customer").List<Customers>();
return (List<Customer>)v;
}
catch
{
session.Close();
throw;
}
finally
{
session.Disconnect();
}
}
}
The Customer also is loaded fine, but the contacts inside are not referenced with the following error:
Initializing[fnh.DataModel.Customer#d2f2d1c5-7d9e-4f77-8b4f-9e200088187b]-failed
to lazily initialize a collection of role:
fnh.DataModel.Kunde._Contact, no session or session was closed
But I cannot understand where the error comes from because the session is closed after executing my HQL error...
The problem is that the contacts are lazy loaded, ie. the collection is not fetched from the database in the initial query. I presume that you are passing the objects directly to the view rather than using a viewmodel? You have a few options, each have their drawbacks.
Keep the session open while the view
is being process (so called Open
Session In View approach). You are probably closing the NH session in the controler at the moment right?
Eager load the whole object graph
using .Not.Lazyload() on the
contacts. (Not recommended)
Copy all the data you need to a view
model in the controller, and pass
this to the view. Use automapper to help you with this.
Update in response to comment:
There is still great benefit in leaving collections lazy loaded. Sure, in this context you don't benefit from lazy loaded contacts on the customer object, becase you need to use them. But in another context you might only need the customer name and Id - and you can rest assured that this wont generate a big query with joins etc.
To utilize Open Session in View you don't have to pass your session to the view explicitly, rather you just pass the object as before, but leave the session open - NH will automatically generate a new query when you try to access the contacts collection. (This works becuase the customer object is still 'attached' to an open session behind the scenes). The only difference here is that you need to close the session not in the controller (where it is currently being closed explicitly with the .close() or implicitly with 'using'). Regarding where to open and close the session - there are different approaches:
In the global.asax - open in Application_BeginRequest and close in Application_EndRequest (see: this article) I'd recomend starting with this for the sake of simplicity if you want to do Open Session in View.
In a http module (basically the same as the last, but modularised) (see this article)
With these last two, you are probably thinking 'But that will mean creating a session for every page request!' - and you would be right, but really how many pages are not going to go to the DB? plus session creation is lightweight once the session factory is created.
Using an Attribute decorating the action method (you basically do the same thing as the last two, but this way you can choose which actions open an NH session). Close the session in OnActionExecuted if you want it closed after the action has completed. Close in OnResultExecuted if you want open session in view.
Of course, many people don't like Open Session in View, becuase you cannot control what queries are being generated purely in the controller - the view is triggering many queries, this can cause unpredictable performance.
3 is subtely different from 2, because in situations where you don't need the lazy loaded collection it is not fetched from the DB. For instance, in this case you might be copying to a ViewModel with the full object graph, but in another situation you might use a ViewModel with just the customer name and Id - in this case the joins for the contacts collection would not needlessly be executed. In my opinion 3 is the proper way to do things - but you end up creating many more objects (becuase of the view objects).

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.

How do I use Fluent Nhibernate many-to-many for optimal performance?

I have a product table that has a many-to-many relation to itself (using a two-column many-to-many table) and I have set it up in Fluent NHibernate with the following code:
public class ProductConfiguration : ClassMap<Product>
{
public ProductConfiguration()
{
Table("Product");
Id(p => p.Id).GeneratedBy.Guid();
Map(p => p.Name).Not.Nullable().Length(254);
Map(p => p.Description).Not.Nullable().Length(1000);
Map(p => p.CreatedAt).Not.Nullable();
HasManyToMany(p => p.CrossSell)
.Table("ProductCrossSell")
.ParentKeyColumn("Id")
.ChildKeyColumn("ProductId");
}
}
My MVC application has two pages that uses this setup:
Index - Uses a generic repository GetAll method to display all products.
Detail - Uses a generic repository GetById method to display one product and any related cross sell products setup in the many-to-many realation.
It looks like NHibernate is set to LazyLoad the many-to-many by default so when I fire up the application and watch it in profiler I can see that it does LazyLoad the many-to-many with the following alert "Use of implicit transactions is discouraged".
How do I get rid of this alert? I couldn't find any information on how to wrap a LazyLoad inside a transaction to get rid the alert. Is it even possible?
Is there a way to not lazyload this by telling NHibernate that whenever I ask for GetById make sure to join the tables a get everything in one query? I tried using .Fetch.Join() in the many-to-many mapping but that also affected my GetAll query which now displays a joined result set as well which is incorrect.
What is the best apprach for this kind of simple scenario?
Thanks
The way to get rid of the warning is to access the object graph and fully populate the UI elements inside a single transaction.
Not by configuration. You can create an HQL query that eager fetches the association and use that query for a specific view. I would stick with lazy loading and not make that optimization unless needed. The HQL would be:
return session.CreateQuery("from ProductionConfiguration pc join fetch pc.CrossSell where pc.Id = ?")
.SetGuid(0, id)
.List<ProductConfiguration>();
All collections are lazily loaded in NHibernate by default.
You must be triggering loading with a call of some kind (maybe even with the debugger watches)