NHibernate - NHibernate.Exceptions.GenericADOException: could not execute query - c#

I have the following exception occurring seemingly at random:
NHibernate.Exceptions.GenericADOException: could not execute query
[ select businesspr0_.BusinessProcessID as col_0_0_, businesspr0_.ProcessNumber as col_1_0_, businesspr1_.Name as col_2_0_, businesspr0_.DateCreated as col_3_0_, actor2_.DisplayName as col_4_0_, usergroup3_.Name as col_5_0_, processele5_.Name as col_6_0_ from BusinessProcess businesspr0_ inner join BusinessProcess businesspr1_ on businesspr0_.DefinitionID=businesspr1_.BusinessProcessID inner join Actor actor2_ on businesspr0_.ActorCreatedID=actor2_.ActorID left outer join UserGroup usergroup3_ on businesspr0_.UserGroupCreatedID=usergroup3_.UserGroupID left outer join Actor usergroup3_1_ on usergroup3_.UserGroupID=usergroup3_1_.ActorID inner join ActiveElement activeelem4_ on businesspr0_.BusinessProcessID=activeelem4_.BusinessProcessID inner join ProcessElement processele5_ on activeelem4_.ProcessElementID=processele5_.ProcessElementID inner join ProcessEmployees processemp6_ on businesspr0_.BusinessProcessID=processemp6_.BusinessProcessID inner join Actor actor7_ on processemp6_.ActorID=actor7_.ActorID inner join UserInGroups users8_ on actor7_.ActorID=users8_.UserGroupID inner join AppUser user9_ on users8_.UserID=user9_.UserID inner join Actor user9_1_ on user9_.UserID=user9_1_.ActorID where businesspr0_.IsDefinition=0 and (businesspr0_.Finished is null) and #p0=user9_.UserID ]
Name:userID - Value:32771
[SQL: select businesspr0_.BusinessProcessID as col_0_0_, businesspr0_.ProcessNumber as col_1_0_, businesspr1_.Name as col_2_0_, businesspr0_.DateCreated as col_3_0_, actor2_.DisplayName as col_4_0_, usergroup3_.Name as col_5_0_, processele5_.Name as col_6_0_ from BusinessProcess businesspr0_ inner join BusinessProcess businesspr1_ on businesspr0_.DefinitionID=businesspr1_.BusinessProcessID inner join Actor actor2_ on businesspr0_.ActorCreatedID=actor2_.ActorID left outer join UserGroup usergroup3_ on businesspr0_.UserGroupCreatedID=usergroup3_.UserGroupID left outer join Actor usergroup3_1_ on usergroup3_.UserGroupID=usergroup3_1_.ActorID inner join ActiveElement activeelem4_ on businesspr0_.BusinessProcessID=activeelem4_.BusinessProcessID inner join ProcessElement processele5_ on activeelem4_.ProcessElementID=processele5_.ProcessElementID inner join ProcessEmployees processemp6_ on businesspr0_.BusinessProcessID=processemp6_.BusinessProcessID inner join Actor actor7_ on processemp6_.ActorID=actor7_.ActorID inner join UserInGroups users8_ on actor7_.ActorID=users8_.UserGroupID inner join AppUser user9_ on users8_.UserID=user9_.UserID inner join Actor user9_1_ on user9_.UserID=user9_1_.ActorID where businesspr0_.IsDefinition=0 and (businesspr0_.Finished is null) and #p0=user9_.UserID]
---> System.IndexOutOfRangeException: col_0_0_
at System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName)
at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name)
at NHibernate.Driver.NHybridDataReader.GetOrdinal(String name)
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String name)
at NHibernate.Type.NullableType.NullSafeGet(IDataReader rs, String[] names, ISessionImplementor session, Object owner)
at NHibernate.Hql.Ast.ANTLR.Loader.QueryLoader.GetResultColumnOrRow(Object[] row, IResultTransformer resultTransformer, IDataReader rs, ISessionImplementor session)
at NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies)
at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies)
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters)
--- End of inner exception stack trace ---
at DotExe.TiCat.DomainModel.Repository.ProcessModel.BusinessProcessDAO.GetProcessForUserGroups(Int32 userID) in D:\Projects\TiCatRNIDS\DotExe.TiCat4\DotExe.TiCat.DomainModel.Repository\ProcessModel\BusinessProcessDAO.cs:line 98
at DotExe.TiCat4.UseCaseRepository.TicketProcess.TicketQueues.RefreshQueue() in D:\Projects\TiCatRNIDS\DotExe.TiCat4\DotExe.TiCat4\UseCaseRepository\TicketProcess\TicketQueues.cs:line 128
Odd part is this System.IndexOutOfRangeException: col_0_0_ which I could not figure out why is happening.
I can update post with some xml mapping if necessary.
Tnx
Update:
Mappings and the query throwing ex:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DotExe.TiCat.DomainModel" namespace="DotExe.TiCat.DomainModel.Process">
<class name="BusinessProcess" table="BusinessProcess">
<id name="BusinessProcessID">
<generator class="hilo"/>
</id>
<property name = "Name" length="1024" type="string" not-null ="false" />
<property name = "Description" length="5000" type="string" not-null ="false" />
<property name = "ProcessNumber" length="500" type="string" not-null ="false" />
<property name = "CommentOnSolve" length="5000" type="string" not-null ="false" />
<property name="IsDefinition" type="bool"/>
<property name="PlaningTimeToSpend" type="long"/>
<property name="SpendTime" type="long"/>
<property name="DateCreated" type="DateTime" not-null="false"/>
<property name="PlanToStart" type="DateTime" not-null="false"/>
<property name="PlanToFinish" type="DateTime" not-null="false"/>
<property name="Started" type="DateTime" not-null="false"/>
<property name="Finished" type="DateTime" not-null="false"/>
<property name="CustomerRequestCallBack" type="bool" not-null="false"/>
<property name="EscalationTimeLimitInPercents" type="int"/>
<property name="IsEscalated" type="bool"/>
<property name="IsInfinite" type="bool"/>
<many-to-one name="WorkingWeek" class="WorkingWeek" column="WorkingWeekID" not-null="false" lazy="false"/>
<many-to-one name="Priority" class="ProcessPriority" column="PriorityID" not-null="false" lazy="false"/>
<many-to-one name="UserCreated" class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorCreatedID" not-null="false" lazy="false"/>
<many-to-one name="UserSolved" class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorSolvedID" not-null="false" lazy="false"/>
<many-to-one name="GroupCreated" class="DotExe.TiCat.DomainModel.Actors.UserGroup" column="UserGroupCreatedID" not-null="false" lazy="false"/>
<many-to-one name="GroupSolved" class="DotExe.TiCat.DomainModel.Actors.UserGroup" column="UserGroupSolvedID" not-null="false" lazy="false"/>
<many-to-one name="Definition" class="BusinessProcess" column="DefinitionID" cascade="none" />
<set name="Elements" lazy="false" cascade="save-update">
<key column="BusinessProcessID"/>
<one-to-many class="ProcessElement"/>
</set>
<set name="ActiveElements" table="ActiveElement" lazy="false" cascade="save-update">
<key column="BusinessProcessID"/>
<many-to-many class="ProcessElement" column="ProcessElementID"/>
</set>
<set name="ProcessConsumers" table="ProcessConsumers" lazy="false" cascade="save-update">
<key column="BusinessProcessID"/>
<many-to-many class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorID"/>
</set>
<set name="ProcessEmployees" table="ProcessEmployees" lazy="false">
<key column="BusinessProcessID"/>
<many-to-many class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorID"/>
</set>
<set name="ProcessSupervisors" table="ProcessSupervisors" lazy="false">
<key column="BusinessProcessID"/>
<many-to-many class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorID"/>
</set>
<set name="Flows" lazy="false" cascade="save-update">
<key column="BusinessProcessID"/>
<one-to-many class="SequenceFlow"/>
</set>
<set name="PriorityTimeRules" lazy="false" cascade="save-update">
<key column="BusinessProcessID"/>
<one-to-many class="DotExe.TiCat.DomainModel.BusinessRules.PriorityTimeRule"/>
</set>
</class>
<class name="ProcessElement" table="ProcessElement">
<id name="ProcessElementID">
<generator class="hilo"/>
</id>
<property name = "Name" length="1024" type="string" not-null ="true" />
<property name="IsDefinition" type="bool"/>
<property name="IsState" type="bool"/>
<many-to-one class="BusinessProcess" name="MemberOf" lazy="false" cascade="save-update" column="BusinessProcessID"/>
<many-to-one class="ProcessElement" name="Definition" column="DefinitionID"/>
<joined-subclass name="Activity">
<key column="ActivityID"/>
<property name = "Name" length="1024" type="string" not-null ="true" />
<property name = "Description" length="4000" type="string" not-null ="false" />
<set name="States" >
<key column="ActivityID"/>
<one-to-many class="ActivityState"/>
</set>
<joined-subclass name="UserTask" table="UserTask">
<key column="UserTaskID"/>
<property name="PlaningTimeToSpend" type="int"/>
<property name="SpendTime" type="int"/>
<property name="PlanToStart" type="DateTime" not-null="false"/>
<property name="PlanToFinish" type="DateTime" not-null="false"/>
<property name="Started" type="DateTime" not-null="false"/>
<property name="Finished" type="DateTime" not-null="false"/>
<set name="Comments">
<key column="UserTaskID"/>
<one-to-many class="TaskComment"/>
</set>
<set name="UserTaskEmployees">
<key column="UserTaskID"/>
<many-to-many class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorID"/>
</set>
<set name="UserTaskSupervisors">
<key column="UserTaskID"/>
<many-to-many class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorID"/>
</set>
<set name="Priorities">
<key column="UserTaskID"/>
<one-to-many class="UserTaskPriority"/>
</set>
<set name="AssignmentRules">
<key column="UserTaskID"/>
<one-to-many class="DotExe.TiCat.DomainModel.BusinessRules.AssignmentRule"/>
</set>
<many-to-one class="DotExe.TiCat.DomainModel.UseCase.ClientApplicationConfig" name="Application" column="ApplicationConfigID"/>
<many-to-one class="UserTaskPriority" name="TaskPriority" column="TaskPriorityID"/>
</joined-subclass>
<joined-subclass name="SendTask">
<key column="SendTaskID"></key>
<many-to-one name="EmailAccount" class="DotExe.TiCat.DomainModel.Config.EmailAccount" column="EmailAccountID" lazy="false"></many-to-one>
<many-to-one name="Template" class="DotExe.TiCat.DomainModel.Templates.Template" column="TemplateID" lazy="false"></many-to-one>
<set name="Actors" lazy="false">
<key column="SendTaskID"></key>
<many-to-many class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorID"></many-to-many>
</set>
<set name="EmailMessages">
<key column="SendTaskID"></key>
<one-to-many class="DotExe.TiCat.DomainModel.Email.EmailMessage"/>
</set>
</joined-subclass>
</joined-subclass>
<joined-subclass name="Gateway">
<key column="GatewayID"/>
<joined-subclass name="ExclusiveGateway">
<key column="ExclusiveGatewayID"/>
</joined-subclass>
</joined-subclass>
<joined-subclass name="Event">
<key column="EventID"></key>
<property name="ExecutionTime" type="DateTime" not-null="false"></property>
<joined-subclass name="StartEvent">
<key column="StartEventID"></key>
</joined-subclass>
<joined-subclass name="EndEvent">
<key column="EntEventID"></key>
</joined-subclass>
</joined-subclass>
</class>
<class name="ActivityState" table="ActivityState">
<id name="ActivityStateID">
<generator class="hilo"/>
</id>
<property name = "Name" length="1024" type="string" not-null ="true" />
<property name = "Description" length="4000" type="string" not-null ="false" />
<many-to-one name="StateOf" class="Activity" column="ActivityID"/>
<property name="IsEndState" type="bool"/>
<property name="IsStartState" type="bool"/>
<property name="IsPauseState" type="bool"/>
<property name="IsProcessSerializationState" type="bool"/>
</class>
<class name="SequenceFlow" table ="SequenceFlow">
<id name="SequenceFlowID">
<generator class="hilo"/>
</id>
<many-to-one class="ProcessElement" name="Source" column="SourceID" lazy="false" cascade="save-update"/>
<many-to-one class="ProcessElement" name="Target" column="TargetID" lazy="false" cascade="save-update"/>
<many-to-one name="Process" class="BusinessProcess" column="BusinessProcessID" cascade="save-update"/>
</class>
<class name="UserTaskPriority" table ="UserTaskPriority">
<id name="UserTaskPriorityID">
<generator class="hilo"/>
</id>
<property name = "Name" length="1024" type="string" not-null ="true" />
</class>
<class name="TaskComment" table ="TaskComment">
<id name="TaskCommentID">
<generator class="hilo"/>
</id>
<property name = "Comment" length="4000" type="string" not-null ="true" />
<many-to-one name="Task" class="ProcessElement" column="UserTaskID"/>
<many-to-one name="Actor" class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorID"/>
<many-to-one name="Process" class="BusinessProcess" column="BusinessProcessID"/>
<many-to-one name="Group" class="DotExe.TiCat.DomainModel.Actors.UserGroup" column="UserGroupID"/>
<property name="CommentTime" type="DateTime" not-null="false" />
</class>
<class name="TransitionLog">
<id name="TransitionLogID">
<generator class="hilo"/>
</id>
<property name="LogDate" type="DateTime"/>
<property name="TaskStarted" type="DateTime" not-null ="false" />
<property name="TaskEnded" type="DateTime" not-null ="false" />
<property name="TimeSpend" type="int" not-null ="false" />
<set name="SendToActors" table="SendToActorLog" lazy="false">
<key column="TransitionLogID"/>
<many-to-many class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorID"/>
</set>
<many-to-one name="Actor" class="DotExe.TiCat.DomainModel.Actors.Actor" column="ActorID" lazy="false"/>
<many-to-one name="Group" class="DotExe.TiCat.DomainModel.Actors.UserGroup" column="UserGroupID"/>
<many-to-one name="FromElement" class="ProcessElement" column="FromElementID" lazy="false"/>
<many-to-one name="ToElement" class="ProcessElement" column="ToElementID"/>
<many-to-one name="Process" class="BusinessProcess" column="BusinessProcessID"/>
<many-to-one name="LogType" class="LogType" column="LogTypeID" lazy="false"/>
<property name="Comment" length="4000" type="string" not-null ="false" />
</class>
<class name="LogType">
<id name="LogTypeID"/>
<property name = "Name" length="1024" type="string" not-null ="false" />
<property name = "DisplayName" length="1024" type="string" not-null ="false" />
</class>
<class name="ProcessPriority">
<id name="ProcessPriorityID"/>
<property name = "Name" length="1024" type="string" not-null ="false" />
<property name = "DisplayName" length="1024" type="string" not-null ="false" />
</class>
<class name="WorkingWeek">
<id name="WorkingWeekID">
<generator class="hilo"/>
</id>
<property name="MondayFrom" type="DateTime" not-null="true"/>
<property name="MondayTo" type="DateTime" not-null="true"/>
<property name="TuesdayFrom" type="DateTime" not-null="true"/>
<property name="TuesdayTo" type="DateTime" not-null="true"/>
<property name="WednesdayFrom" type="DateTime" not-null="true"/>
<property name="WednesdayTo" type="DateTime" not-null="true"/>
<property name="ThursdayFrom" type="DateTime" not-null="true"/>
<property name="ThursdayTo" type="DateTime" not-null="true"/>
<property name="FridayFrom" type="DateTime" not-null="true"/>
<property name="FridayTo" type="DateTime" not-null="true"/>
<property name="SaturdayFrom" type="DateTime" not-null="true"/>
<property name="SaturdayTo" type="DateTime" not-null="true"/>
<property name="SundayFrom" type="DateTime" not-null="true"/>
<property name="SundayTo" type="DateTime" not-null="true"/>
<property name="Is24x7" type="bool"/>
<property name="Name" type="string" length="256" not-null="true"/>
<!--<set name="BusinessProcesses" cascade="none" lazy="false">
<key column="WorkingWeekID"/>
<one-to-many class="BusinessProcess"/>
</set>-->
</class>
</hibernate-mapping>
And the query:
IList<object[]> result =
session.CreateQuery(" select bp.BusinessProcessID, bp.ProcessNumber, def.Name as Definition," +
" bp.DateCreated, actor.DisplayName as UserCreated," +
" group.Name as GroupCreated, task.Name as ActiveTask " +
" from BusinessProcess bp " +
" join bp.Definition as def " +
" join bp.UserCreated as actor " +
" left join bp.GroupCreated as group " +
" join bp.ActiveElements as task" +
" join bp.ProcessEmployees as res" +
" join res.Users as users " +
" where bp.IsDefinition = false " +
" and bp.Finished is null" +
" and :userID = users.ActorID").SetParameter("userID", userID).List<object[]>();

I've gotten this error when I tried to map a null value to a non-nullable property (i.e. an integer). make sure that either you make sure that the sql value is non-nullable, or just make the c# property nullable (public virtual int? NumberOfPoints { get; set; })

I don't know the reason for this. But there is something odd in the mappings: Activity has the property "Name", which is already mapped in the base class. This may lead to index out of range. There are probably more problems around.
Be very careful with sets like "Actors" which are many-to-many and do not specify a table name. There is a high risk of defining another "Actors" set which will get conflicting table definitions.
Unrelated for sure: some sets should be inverse, because there is a reference from the element to the parent.

Related

Nhibernate Queryover multi level model

lets say i have this database schema
[User] -1----n-> [Resource] -1----n-> [ResourceVersion]
and i want to select this using Nhibernate in one database roundtrip for user by username but select resourceVersions with future doesn work. How to hydrate collection of collections in one roundtrip using Futures? I prefer QueryOver or Criteria over HQL. I am using nHibernate 4.0.
public virtual User GetUserResources(string username)
using (ISession session = GetSession())
{
Resource resAlias = null;
User userAlias = null;
var result = session.QueryOver(() => userAlias)
.JoinQueryOver(x => x.Resources, () => resAlias)
.JoinQueryOver(() => resAlias.Versions)
.Where(() => userAlias.Login == username)
.Future<User>(); //THIS DOESNT WORK
var user = session.QueryOver<User>()
.Fetch(x => x.Resources).Eager
.Where(x => x.Login == username)
.SingleOrDefault<User>();//with this i can select user and resources
return user;
}
Mappings:
USER:
<class name="User" table="[User]">
<id name="Id" type="Int32">
<generator class="identity" />
</id>
<property name="Name">
<column name="Name" sql-type="varchar(100)" />
</property>
<property name="Email">
<column name="Email" sql-type="varchar(255)" />
</property>
<property name="Login">
<column name="Login" sql-type="varchar(50)" />
</property>
<property name="PasswordHash">
<column name="PasswordHash" sql-type="varchar(100)" />
</property>
<property name="CreateDate">
<column name="CreateDate" sql-type="datetime" />
</property>
<bag name="Resources" lazy="true" fetch="subselect" cascade="all-delete-orphan">
<key column="UserResource"/>
<one-to-many class="Resource" />
</bag>
</class>
RESOURCE:
<class name="Resource" table="[Resource]" abstract="true">
<id name="Id" type="Int64">
<generator class="identity" />
</id>
<discriminator column="Type"
not-null="true"
type="String" />
<bag name="Versions" cascade="all-delete-orphan" inverse="true" lazy="true" order-by="ActiveFrom DESC">
<key column="ResourceId" not-null="true"/>
<one-to-many class="Version"/>
</bag>
<subclass name="Resource1" discriminator-value="Res1" />
<subclass name="Resource2" discriminator-value="Res2" />
</class>
VERSION:
<class name="Version" table="Version">
<id name="Id" type="long">
<!--<column name="Id" sql-type="bigint"/>-->
<generator class="identity" />
</id>
...
<many-to-one name="Resource"
class="Resource"
column="ResourceId"/>
<property name="ActiveFrom">
<column name="ActiveFrom" sql-type="datetime" />
</property>
<property name="ActiveTo">
<column name="ActiveTo" sql-type="datetime"/>
</property>
...
Only query executed according to intelli trace in visual studio is this one:
SELECT this_.Id AS Id0_1_ ,
this_.Name AS Name0_1_ ,
this_.Email AS Email0_1_ ,
this_.Login AS Login0_1_ ,
this_.PasswordHash AS Password5_0_1_ ,
this_.CreateDate AS CreateDate0_1_ ,
resource2_.UserResource AS UserResource3_ ,
resource2_.Id AS Id3_ ,
resource2_.Id AS Id4_0_ ,
resource2_.Type AS Type4_0_
FROM
[User] this_ LEFT OUTER JOIN [Resource] resource2_
ON this_.Id
=
resource2_.UserResource
WHERE this_.Login
=
#p0;
and in #p0 is username i pass to method. No sign of versions at all which i find a little odd.
you are never iterating the IEnumerable returned by the future so it never executes it. I don't have NH 4.0 here right now but the following might work
public virtual User GetUserWithResources(string username)
{
using (ISession session = GetSession())
{
Resource resAlias = null;
return session.QueryOver<User>()
.Where(user => user.Login == username)
.Left.JoinQueryOver(x => x.Resources)
.Left.JoinQueryOver(res => res.Versions)
.TransformUsing(Transformers.DistinctRootEntity)
.List<User>().SingleOrDefault();
}
}

NHibernate Issue: List of subclass picking up all classes in table-per-class-hierarchy

We have a class, SpecialContainer (itself a subclass of Container), that has a list of contents of type TypeThreeContent:
class SpecialContainer {
private IList<TypeThreeContent> _contents = new List< TypeThreeContent >();
}
TypeThreeContent is just a subclass of BaseContent, both of which have some uninteresting properties I've omitted for simplicitly. Here's the mapping:
<hibernate-mapping xmlns="urn:nhibernate-mapping-​2.2">
<class xmlns="urn:nhibernate-mapping-​2.2" discriminator-value="0" name="AbstractBaseContent" table="tblContent">
<id access="field" name="_id" type="System.Int32" unsaved-value="0">
<column name="ContentID" />
<generator class="identity" />
</id>
<discriminator type="Int32">
<column name="FormsType" />
</discriminator>
<subclass name="TypeOneContent" discriminator-value="1">
</subclass>
<subclass name="TypeTwoContent" discriminator-value="2">
</subclass>
<subclass name="TypeThreeContent" discriminator-value="3">
</subclass>
</class>
<class xmlns="urn:nhibernate-mapping-​2.2" discriminator-value="-1" name="BaseContainer" table="tblContainers">
...
<subclass name="SpecialContainer" discriminator-value="1">
<join table="tblSpecialContainers">
...
<bag access="field" cascade="none" inverse="true" lazy="true" name="_contents">
<key>
<column name="ContainerId" />
</key>
<one-to-many class="TypeThreeContent" />
</bag>
</join>
</class>
</hibernate-mapping>
The problem I'm having is that rows with other discriminator values (i.e. 1 and 2) are getting picked up in NH's fetching of the list. I used NHProf to verify that the SQL query is not specifying a discriminator in its criteria:
SELECT ...
FROM dbo.tblContent content0_
WHERE content0_.SpecialContainerId = 140 /* #p0 */
What's going on here?

NHibernate collections: Update doesn't cascade

Currious behavior here. If I create a new HashCode, save the HashCode, then add a Transaction to the Transactions collection, the cascade is failing on Update. The Transaction object doesn't appear in the DB, and, curiously, the HashCode object's properties aren't updated either!
I have no idea what could be causing this. Here is the relevant mapping:
<class name="MyProject.HashCode, MyProject" table="HashCodes">
<id column="Id" name="Id">
<generator class="native" />
</id>
<many-to-one name="User" column="UserId" class="MyProject.User, MyProject" />
<property name="Hash" />
<property name="PasswordHash" />
<property name="InitialValue" update="false" />
<property name="CurrentValue" update="true" />
<property name="ClaimedDate" />
<property name="ClaimId" column="RowGuid" generated="insert" />
<bag name="Transactions" table="Transactions" cascade="all" inverse="true">
<key column="HashCodeId" />
<many-to-many column="Id" class="MyProject.Transaction, MyProject" />
</bag>
</class>
<class name="MyProject.Transaction, MyProject" table="Transactions">
<id column="Id" name="Id">
<generator class="native" />
</id>
<many-to-one name="HashCode" column="HashCodeId" class="MyProject.HashCode, MyProject" />
<property name="AmountCharged" />
<property name="AmountBilled" />
<property name="PreviousBalance" />
<property name="Memo" />
<property name="TransactionDate" generated="insert" update="false" />
</class>
Here is the test case that is failing, too, in case it's relevant:
[Test]
public void TransactionsCascadeWhenUpdatingHashCodes()
{
var user = TestFactory.CreateUser();
var hashCode = TestFactory.CreateHashCode(user);
var transaction = new Transaction
{
AmountBilled = hashCode.CurrentValue,
AmountCharged = decimal.Subtract(hashCode.CurrentValue, decimal.Multiply(hashCode.CurrentValue, 0.03M)),
HashCode = hashCode,
Memo = "This is a test",
PreviousBalance = hashCode.CurrentValue,
TransactionDate = DateTime.Now
};
hashCode.Transactions.Add(transaction);
// Now try to save it.
var hashCodeRepository = new HashCodeRepository(Session);
hashCodeRepository.Update(hashCode);
// Now see if that transaction is good to go
Assert.IsTrue(hashCode.Transactions[0].Id > 0);
// See if that transaction got persisted.
var loadedTransaction = Session.Load<Transaction>(hashCode.Transactions[0].Id);
Assert.IsNotNull(loadedTransaction);
}
// The repository method that is called...
public virtual void Update(TObj obj)
{
CurrentSession.Update(obj);
}
Anyone have any suggestions or ideas?
You are not flushing the session anywhere.
Change your update method to the following:
public virtual void Update(TObj obj)
{
using (ITransaction tx = CurrentSession.BeginTransaction())
{
CurrentSession.SaveOrUpdate(obj);
tx.Commit();
}
}

Why NHibernate fails to lazy load depending on SetMaxResults argument?

we have one to many to one relationship which we are trying to implement in NHibernate. This is a rephrase of my colleague's question.
There is Block with a collection of GroupPartnerInterests every of which has a Company. Following test method passes with SetMaxResults(3) but fails with SetMaxResults(5). Exception is
NHibernate.LazyInitializationException:
Initializing[Model.EntityClasses.BaseBlock#100000121437]-failed
to lazily initialize a collection of
role:
Model.EntityClasses.BaseBlock.GroupPartnerInterests,
no session or session was closed.
Question is why does SetMaxResults's argument matter?
The test method is:
[TestMethod]
public void TestGroupPartnerInterests()
{
using ( ISession session = SessionFactory.OpenSession() )
{
IList<Block> blocks = session
.CreateCriteria( typeof( Block ) )
.SetMaxResults( 5 ).List<Block>();
foreach ( var block in blocks )
{
TestContext.WriteLine( block.BlockId + " " + block.BlockName );
if ( block.GroupPartnerInterests != null )
{
foreach ( var gpi in block.GroupPartnerInterests )
{
TestContext.WriteLine( gpi.Company.CompanyName );
}
}
}
}
}
Configuration XMLs:
<class name="Block" table="[BLOCK]">
<id name="BlockId" column="GA_ID" access="field.camelcase-underscore" >
<generator class="assigned"/>
</id>
... data properties ...
<many-to-one name="Contract" access="field.camelcase-underscore"
fetch="select" unique="true">
<column name="EPC_ID"/>
</many-to-one>
<many-to-one name="Country" access="field.camelcase-underscore"
fetch="select" cascade="none">
<column name="COUNTRY_ID"/>
</many-to-one>
<set name="GroupPartnerInterests" access="field.camelcase-underscore"
cascade="all-delete-orphan" fetch="select">
<key property-ref="GroupId">
<column name="PAR_ID"/>
</key>
<one-to-many class="GroupPartnerInterest"/>
</set>
</class>
<class name="GroupPartnerInterest" table="[GROUP_PARTNER_INTERESTS]">
<composite-id >
<key-property name="GroupId" column="PAR_ID" />
<key-property name="CompanyId" column="PU_ID" />
</composite-id>
... data properties ...
<many-to-one name="Company" access="field.camelcase-underscore"
fetch="select" cascade="none">
<column name="PU_ID"/>
</many-to-one>
</class>
<class name="Company" table="[COMPANY]">
<id name="CompanyId" column="PU_ID" access="field.camelcase-underscore" >
<generator class="assigned"/>
</id>
... data properties ...
<set name="GroupPartnerInterests" access="field.camelcase-underscore"
cascade="all-delete-orphan" inverse="true" fetch="select">
<key>
<column name="PU_ID"/>
</key>
<one-to-many class="GroupPartnerInterest"/>
</set>
</class>
I'm not certain if this is the problem in your specific case, but in general, the use of implicit transactions is discouraged when using Nhibernate as discussed here. Your data access should always follow this pattern:
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction())
{
// execute code that uses the session
tx.Commit();
}

<Sql-insert> for sub-classes

I have an object mapped as follows:
<class name="A" table="TableA">
<id name="ID" column="AId" type="Int32" unsaved-value="0">
<generator class="native" />
</id>
<discriminator column="Type" type="Int32" />
<property name="Description" />
</class>
<subclass name="B" discriminator-value="0" extends="A">
<property name="B_specific_properties />
</subclass>
<subclass name="C" discriminator-value="1" extends="A">
<property name="C_specific_properties />
</subclass>
I'm planning to use a stored procedure to do the insert, and currently both B and C uses the same stored procedure to insert into the table. My question is with the objects mapped as above, does the sql-insert belong to class A? If it does, is it expecting to do a save() on object A? How can I make it recognize the additional properties of B and C when I'm saving?
OR
Is using one stored procedure to save for the two sub-classes an impossible task, although they belong to the same table?
Thank you very much for any input!
I will show you how I did this
<class name="CompanyCharacteristic" table="firma_cecha" abstract="true" discriminator-value="not null">
<id name="Id" column="id_firma_cecha">
<generator class="native" />
</id>
<discriminator column="typ_cechy"/>
<property name="FriendlyTypeName" column="typ_cechy" insert="false" update="false"/>
<property name="All" column="cecha_all" access="nosetter.camelcase"/>
<!-- Auditing data -->
<property name="UserInserted" access="nosetter.camelcase" column="user_insert" insert="true" update="false"/>
<property name="DateInserted" column="data_insert" generated="insert" insert="false" update="false"/>
<subclass discriminator-value="Akceptacja kart płatniczych" name="CreditCardAcceptanceCharacteristic">
<property name="DoesIt" column="cecha_dec_1" not-null="true" type="DataAccess.NHibernate.Infrastructure.UserTypes.EnumValueInfoUserType`1[[Domain.Characteristics.YesNoNumericEnum, Domain]], DataAccess"/>
<sql-insert>
DECLARE #returned decimal(20,0)
exec sp_FirmaCecha_Add
#TypCechy = 'Akceptacja kart płatniczych',
#CechaAll = ?,
#UserInsertZrodlo = ?,
#CechaDec1 = ?,
#IdFirmaCecha = #returned OUTPUT
SELECT #returned
</sql-insert>
<sql-update>
exec sp_FirmaCecha_Update
#TypCechy = 'Akceptacja kart płatniczych',
#UserUpdateZrodlo = ?,
#CechaDec1 = ?,
#IdFirmaCecha = ?
</sql-update>
<sql-delete>
exec sp_FirmaCecha_Delete ?
</sql-delete>
</subclass>
<sql-insert>
raiserror ('CompanyInfoCharacteristic nie moze byc dodwana za pomocą NHibernate',11,1)
</sql-insert>
<sql-update>
raiserror ('CompanyInfoCharacteristic nie moze byc aktualizowana za pomocą NHibernate.',11,1)
</sql-update>
<sql-delete>
raiserror ('CompanyInfoCharacteristic nie moze byc kasowana za pomocą NHibernate.',11,1)
</sql-delete>
As you can see every subclass need to have its own sql statements. Base class can have its own statements also. In this example I do not allow to modify Base class instances through NHibernate

Categories

Resources