This method gets called.
public IList<MyStuff> GetMyStuff(Int64 MyStuffId)
{
ICriteria criteria = NHibernateSessionManager.Instance.GetSession().CreateCriteria(typeof(MyStuff));
criteria.Add(Expression.Eq("x", MyStuff));
return criteria.List<MyStuff>();
}
But if I profile SQL Server, I can see that NHibernate doesn't try to access the server.
No errors are thrown. It is just the criteria.List() simply returns 0 rows.
MyStuff is a class
public class MyStuff {
public virtual int Id { get; set; }
public virtual int x { get; set; }
... more attributes ....
public override int GetHashCode() {
return (GetType().FullName + "|" + Id.ToString()).GetHashCode();
}
}
And MyStuff is a HBM mapping:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-import="false" assembly="MyStuff" namespace="My.Stuff" default-lazy="false">
<class name ="MyStuff" table="dbo.viewMyStuff" dynamic-update="false" lazy="false">
<cache usage="read-only"/>
<id name="Id" column="Id" type="int">
<generator class="native" />
</id>
<property name="x" />
.... other properties
</class>
</hibernate-mapping>
The following works just:
select * from viewMyStuff
NHibernate does just fine with other classes/views in the same project.
In fact if I intentionally typo the "table" in the HBM file to "XviewXMyStuffX" NHibernate doesn't have any problem with the typo. Why is NHibernate simply ignoring the expected attempt to access my database view?
I turns out that the view treats the attribute "x" as a string. But in nHibernate I define it as a Int64. These type differences much be causing the criteria to fail. But without any reported error?
Double check that your query really tries to use the exact class you intenden, and that the mapping also applies to exactly the same class. Beware of classes with same name in different namespace or assembly, for instance. One cause of this type of issue is if you attempt to query for a class that is in fact not mapped in NHibernate - then NHibernate will return en empty result, and not an error.
Oh, and have you tried without the cache-element to rule that out?
I have two objects, Case and Note. A Case can have gobs of Notes, like, in the thousands. We are trying to load them asynchronously, in batches, and stream them to the UI so there is no delay waiting for them all to load.
The class/mappings are
public class Case
{
public virtual IList<Note> Notes { get; protected set; }
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="SCMS.TAMS.BusinessEntities" namespace="SCMS.TAMS.BusinessEntities">
<class name="Case" table="Cases">
<bag name="Notes" inverse="true" cascade="all" lazy="true">
<key column="CaseID" />
<one-to-many class="Note" />
</bag>
</class>
</hibernate-mapping>
public class Note
{
public virtual Case Case {get; set;}
public virtual long CaseId {get; set;}
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="SCMS.TAMS.BusinessEntities" namespace="SCMS.TAMS.BusinessEntities" default-lazy="true">
<class name="Note" table="CaseNotes">
<many-to-one name="Case" column="CaseID"/>
<property name="CaseId" column="CaseID" />
</class>
</hibernate-mapping>
Now, when I call
NHibernateSession.Query<Note>().Where(n => n.CaseId == 123).Skip(0).Take(10).ToList();
to load the first 10 Notes for Case 123, the thing loads the Case object, which takes about 30 seconds because there's lots of other things on it, and other logic when it gets loaded, etc., none of which I need/want at this time. All I want/need are the 10 Notes.
I've tried all sorts of variations on this mapping and none of them have worked. What am I doing wrong here?
How are you using this query? is it some thing for the UI? liking showing in a grid or something? or are you performing business logic in a component?
Either way you want to project into another object. Your query right now returns a list of notes which is then going to load that parent object per the mappings.
So if you are using this query to send the information to the UI of an asp.net mvc application, project directly into your view model
NHibernateSession.Query<Note>().Where(n => n.CaseId == 123).Select(n => new SomeViewModel { Prop1 = n.Prop1, Prop2 = n.Prop2 ...}).Skip(0).Take(10).ToList();
or create an anonymous object
NHibernateSession.Query<Note>().Where(n => n.CaseId == 123).Select n => new { n.Prop1, n.Prop2, ...}).Skip(0).Take(10).ToList();
This will keep the parent object from loading. It also has the added benefit that you are only querying the information you need because the query be limited to the data you are projecting.
Important to know is that if all above is true...
this is the real mapping (which is not it is just an obvious extract)
<class name="Note" table="CaseNotes">
<many-to-one name="Case" column="CaseID"/>
...
this is the class (again extract without ID)
public class Note
{
public virtual Case Case {get; set;}
public virtual long CaseId {get; set;}
}
and that would be a UNIT TEST statement to load notes:
var list = NHibernateSession
.Query<Note>()
.Where(n => n.CaseId == 123)
.Skip(0).Take(10)
.ToList();
then NHibernate will NEVER load the Case object. Never. Because:
NHibernate is lazy, just live with it
The reason, the trigger to load related reference (Case property) must be something explicit.
Mostly:
there is usage of the Case object somewhere. E.g. in override of the GetHashCode() the Case.ID is used
Or:
there is a serialization or DTO conversion which does touch the Case property
In those case, NHibernate must load that...
So, create few unit tests with basic queries and assure that your the is really as shown above. Then it will work as expected
Firstly, I'm sorry for asking this question AGAIN when there are several resources that (theoretically)
explain it. I've listed the references I used at the bottom of this question, hopefully they will
help someone else if nothing else.
I am trying to execute a simple stored proc on an oracle 11 database. My intent is to create
a List{T} object from data returned via a SYS_REFCURSOR.
I get this error message as soon as I try to to create an nhibernate session object:
{"Errors in named queries: {GET_COLLATERAL}"}
Here is my mapping. The namespace, schema, assembly, query name are spelled correctly.
The file is named GetCollateral.hbm.xml and is marked as an embedded resource.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Poolman" namespace="Poolman.Entities" schema="poolman_own">
<sql-query name="GET_COLLATERAL" callable="true">
<return class="Poolman.Entities.IDNamePair">
<return-property name="ID" column="sort_order"></return-property>
<return-property name="Name" column="collateral"></return-property>
</return>
{GET_COLLATERAL(?)}
</sql-query>
</hibernate-mapping>
After quite a bit of troubleshooting I managed to get a session object to be created
by removeing the return element from the mapping as shown below. Apparently there is something wrong with it
but I don't know what.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Poolman" namespace="Poolman.Entities" schema="poolman_own">
<sql-query name="GET_COLLATERAL" callable="true">
{GET_COLLATERAL(?)}
</sql-query>
</hibernate-mapping>
I don't expect to get a result set back with no return mapping, but using the mapping above allows nhibernate to create its session obect and to try to execute the query. However, nHibernate cannot get it's parameters right. I get this error message:
{"Expected positional parameter count: 1, actual parameters: [] [{GET_COLLATERAL(?)}]"}
I've tried:
CALL GET_COLLATERAL()
BEGIN GET_COLLATERAL(); END;
The above wrapped in CDATA
Here is my stored proc:
create or replace
PROCEDURE GET_COLLATERAL(p_cursor OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p_cursor for
SELECT collateral, sort_order
FROM
(
-- Long query omitted. The query executes when pasted into a command window.
) ORDER BY sort_order ;
END;
Here is my entity class. This class does not map to any one table but I tried to create a mapping for it anyhow.
namespace Poolman.Entities
{
public class IDNamePair
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
}
}
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping assembly="Poolman"
namespace="Poolman.Entities"
schema="poolman_own"
xmlns="urn:nhibernate-mapping-2.2">
<class name="IDNamePair" table="x">
<id></id>
<property name="ID" column="sort_order"/>
<property name="Name" column="collateral"/>
</class>
</hibernate-mapping>
Here is the code I'm using to call the query:
public List<Entities.IDNamePair> GetCollateral()
{
IQuery query = (IQuery)Session.GetNamedQuery("GET_COLLATERAL");
List<Entities.IDNamePair> list = new List<Entities.IDNamePair>();
System.Collections.IList result = query.List();
list = result.OfType<Entities.IDNamePair>().ToList();
return list;
}
I really appreciate any help with this. I'm stuck.
Here are links to other resources I've found, none can help me however:
Sorry stackoverflow only allows me to post two links:
Oracle stored procedures, SYS_REFCURSOR and NHibernate
http://www.techonthenet.com/oracle/questions/cursor1.php
Maybe two considerations could be done for this problem:
First, the Stored Procedure which has no input parameters, does not require to be called with parameters, so as this questions comment says calling sp with out ref cursor call the SP in this way { call GET_COLLATERAL }
Second, to use a "not mapped" class as result set, you should instruct nhibernate about that "not mapped" class, so try to add <import class="FullClassName" rename="ClassNameMayBeRenamed"/> at top of the mapping file
So this could be a mapping for this SP:
....
<import class="Poolman.Entities.IDNamePair" />
....
<sql-query name="GET_COLLATERAL" callable="true">
<return class="Poolman.Entities.IDNamePair">
<return-property name="ID" column="sort_order"></return-property>
<return-property name="Name" column="collateral"></return-property>
</return>
{ call GET_COLLATERAL}
</sql-query>
The calling code could be a simple code to call a SP through NHibernate..
I hope this could be your solution. Please let me know if I'm wrong
Regards
I'm having a hard time trying to get my stored procedure works with NHibernate. The data returned from the SP does not correspond to any database table.
This is my mapping file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainModel" namespace="DomainModel.Entities">
<sql-query name="DoSomething">
<return class="SomeClass">
<return-property name="ID" column="ID"/>
</return>
exec [dbo].[sp_doSomething]
</sql-query>
</hibernate-mapping>
Here is my domain class:
namespace DomainModel.Entities
{
public class SomeClass
{
public SomeClass()
{
}
public virtual Guid ID
{
get;
set;
}
}
}
When I run the code, it fails with
Exception Details: NHibernate.HibernateException: Errors in named queries: {DoSomething}
at line 80
Line 78: config.Configure(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NHibernate.config"));
Line 79:
Line 80: g_sessionFactory = config.BuildSessionFactory();
When I debug into NHibernate code, it seems that SomeClass is not added to the persister dictionary because there isn't a class mapping (only sql-query) defined in hbm.xml. And later on in CheckNamedQueries function, it is not able to find the persistor for SomeClass.
I've checked all the obvious things (e.g. make hbm as an embedded resource) and my code isn't too much different from other samples I found on the web, but somehow I just can't get it working. Any idea how I can resolve this issue?
Well, where is your class mapping for SomeClass?
You still need to map it. Read http://nhibernate.info/doc/nh/en/index.html#querysql-load.
Look at using a class mapping with a subselect block. I found this in the Java documentation but maybe it will work for .Net too.
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/mapping.html (scroll down to section 5.1.3)
I previously asked a question regarding modeling of a situation with Users, Items, and UserRatings. In my example UserRatings are associated with one User and one Item. A good answer was provided by Nathan Fisher and I've included the model he suggested below.
But I now have a question regarding retrieval of these objects.
The model links the entities by holding references to the entities.My question is, how best do I retrieve a particular UserRating to be updated? In this situation I would have the userID (from the asp.net auth session), and the itemID (from the URL). Also, there could be 1000s of ratings per user or item.
Back in the old school this would be as simple as one update query 'where x = userID and y=itemID. Easy. However the best way to accomplish this in NHibernate using a proper object model is not so clear.
A) I understand that I could create a repository method GetRatingByUserAndItem, and pass it both a User and Item object, which it would do an HQL/criteria query on to retrieve the Rating object. However to do this I assume that I would first need to retrieve User and the Item from the ORM before passing these back to the ORM in the query. I would then get the UserRating object, update it, and then have the ORM persist the changes. This seems ridiculously inefficent to me, compared to the old school method.
B) Maybe I could just new-up the UserRating object, and do a createorupdate type call the ORM (not sure on exact syntax). This would be better, but presumably I would still need to first retrieve the User and Item, which is still pretty inefficient.
C) Perhaps I should just retrieve the User (or the Item) from the ORM, and find the correct UserRating from its UserRatings collection. However, if I do that, how do I make sure that I'm not retrieving all of the UserRatings related to that User (or Item), but just the one related to the specific item and specific user?
D) It occured to me that I could just drop the full-blown references to User and Item from UserRating in the model, and instead have primitive references (UserID and ItemID). This would allow me to do something as simple as the oldschool method. Very tempting, but this just doesn't seem right to me - not very Object Oriented (and surely that's the main reason we are using an ORM in the first place!)
So, can anyone offer some sage advice? Am I on the right track with any of the options above? Or is there a better way that I have not considered?
Thanks in advace for your help! :)
UPDATE:
I've just posted a bounty for this, and understand this better, I would also like to know, using a similar approach, how best to perform the following queries:
Retrieve all the Items which a user had NOT rated.
Retrieve the Item(s) and Item rating(s) which the user had rated the lowest.
The Model follows below:
public class User
{
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
public virtual IList<UserRating> Ratings { get; set; }
}
public class Item
{
public virtual int ItemId { get; set; }
public virtual string ItemName { get; set; }
public virtual IList<UserRating> Ratings { get; set; }
}
public class UserRating
{
public virtual User User { get; set; }
public virtual Item Item { get; set; }
public virtual Int32 Rating { get; set; }
}
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Test" namespace="Test" >
<class name="User">
<id name="UserId" >
<generator class="native" />
</id>
<property name="UserName" />
<bag name="Ratings" generic="true" inverse="true" table="UserRating">
<key column="UserId" />
<one-to-many class="UserRating"/>
</bag>
</class>
<class name="Item" >
<id name="ItemId" >
<generator class="native" />
</id>
<property name="ItemName" />
<bag name="Ratings" generic="true" inverse="true" table="UserRating">
<key column="ItemId" />
<one-to-many class="UserRating"/>
</bag>
</class>
<class name="UserRating" >
<composite-id>
<key-many-to-one class="User" column="UserId" name="User" />
<key-many-to-one class="Item" column="ItemId" name="Item" />
</composite-id>
<property name="Rating" />
</class>
</hibernate-mapping>
Normally, when you use an ORM, you want to implement the business logic (say: changing data) object oriented. This requires to load the objects from the database. NH allows you to load them once, and change it without any reference to any database related stuff, but just changing property values.
This said, it is not always as easy as that. Sometimes there are performance reasons which requires other ways to update data.
You could use HQL updates or even SQL updates.
Another, more classical way to accomplish this is to only load UserRatings. This requires to make it an independent entity (it needs an id, avoid the composite id anyway, replcae it with many-to-one references). Then you filter the UserRatings by user and item, load the items you want to change in the database, and change them using object oriented programming.
It is always a trade-off between performance and object oriented programming. You should try to make it as OO as possible, and only do optimizations if it is needed. Maintainability is important.
I would avoid moving foreign keys to the domain model.
I would choose option C. Your concerns about performance indicate you may be optimizing prematurely. I think it would be fine for you to have a GetUser(int userId) method, then look for the appropriate item in its Ratings collection, and update it.
This does, however, bring up a common problem that ORMs suffer called the N+1 SELECT problem. Looking at each UserRating to find the appropriate one would likely result in one SELECT statement per UserRating. There are several ways to address this. One being to change your mapping file to either disable lazy loading of the Ratings collection, or else load it using 'join' fetching - see this section of the NHibernate documentation.
Using HQL your query would look like this
Select From UserRating
Where ItemId=: #ItemId
and UserId=: #UserId
This will give you the UserRating object that you are after then you can updated in and save it as necessary
And alternative would be
Session.CreateCriteria(typeof(ClassLibrary1.UserRating))
.Add(Expression.Sql(String.Format("ItemId={0}",UserId)))
.Add(Expression.Sql(String.Format("UserId={0}",ItemId)))
.List<ClassLibrary1.UserRating>();
This was the simplest way I could get this to work. I am not happy with the embedded strings but it works.
public void UpdateRating( int userId, int itemId, int newRating, ISession session )
{
using( var tx = session.BeginTransaction())
{
var ratingCriteria = session.CreateCriteria<UserRating>()
.CreateAlias( "Item" "item" )
.CreateAlias( "User" "user" )
.Add( Restrictions.Eq( "item.ItemId", itemId ) )
.Add( Restrictions.Eq( "user.UserId", userId ) );
var userRating = ratingCriteria.UniqueResult<UserRating>();
userRating.Rating = newRating;
tx.Commit();
}
}
You will need to test this as it has been a while since I used the criteria api but essentially what this does is creates alias for two association paths and then using those aliases, adds restrictions to the User and Item so that you only get the UserRating you are interested in.
Everything is done inside a transaction and by relying on NHibernate's dirty-state tracking, the change will be flushed to the database when the transaction commits.
Depending on the version of NHibernate you are using, you could also query using NHLinq or the NHLambda stuff that has been integrated and is now accessible via session.QueryOver<T>.
To retrieve a list of all the items that a user has not rated, you will need to use a subquery to identify all of the items the user has rated and then apply a not in clause to all of the items ( essentially get me all the items not in the items the user has rated ).
var ratedItemsCriteria = DetachedCriteria.For<UserRating>()
.CreateAlias( "Item" "item" )
.SetProjection( Projections.Property( "item.ItemId" ) )
.CreateCriteria( "User" )
.Add( Restrictions.Eq( "UserId", userId ) );
var unratedItemsCriteria = session.CreateCriteria<Item>()
.Add( Subqueries.PropertyNotIn( "ItemId", ratedItemsCriteria ) );
var unratedItems - unratedItemsCriteria.List<Item>();
In general, I think most of your problems could be resolved by judicious application of google, nhforge and the nhibernate user mailing list.
Query the database (using HQL, Criteria, SQL query, etc.) for the UserRating you want to modify
Change the UserRating however you like
Commit your changes
In pseudocode, this would look something like this:
using (ITransaction transaction = session.BeginTransaction())
{
UserRating userRating = userRatingRepository.GetUserRating(userId, itemId);
userRating.Rating = 5;
transaction.Commit();
}
This will involve two queries (as opposed to the one query "old school" solution). The first query (which happens in the GetUserRating call) will run a SQL "SELECT" to grab the UserRating from the database. The second query (which happens on transaction.Commit) will update the UserRating in the database.
GetUserRating (using Criteria) would look something like this:
public IList<UserRating> GetUserRating(int userId, int itemId)
{
session.CreateCriteria(typeof (UserRating))
.Add(Expression.Eq("UserId", userId))
.Add(Expression.Eq("ItemId", itemId))
.List<UserRating>();
}
I see that this Q has not been marked as answered, so I'll give it a shot. In my opinion, you have to look a lot at how the objects are used. It seems to me that you'd relate a UserRating more to an Item than to a user, simply because you'd display it next to the item in a UI. It doesn't feel that important to always display it for a user.
That is why I would remove the list of ratings from the user:
public class User
{
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
}
If you want the ratings for a user, you'd get that through a repository.
I'd leave the Item class unchanged, since you always want to see the ratings with an item:
public class Item
{
public virtual int ItemId { get; set; }
public virtual string ItemName { get; set; }
public virtual IList<UserRating> Ratings { get; set; }
}
The UserRating class could be completely disconnected from the Item and User classes. Just keep the ids in there, so you could retrieve an Item or User from a repository if you need to:
public class UserRating
{
public virtual int UserId { get; set; }
public virtual int ItemId { get; set; }
public virtual Int32 Rating { get; set; }
}