简体   繁体   中英

Exception NHibernate Composite-ID Child Cascade

I have a problem with nhibernate for some time and have never been able to solve, try to clearly describe the problem. I would like to save and edit a list of objects connected via FK to the parent object in cascade using the functionality of NHibernate. I can not understand where I'm wrong, who can help me?

The tables on the DB are:

CREATE TABLE [Evento].[Eventi](
    [CodiceEvento] [varchar](20) NOT NULL,
    [CodiceTipoEvento] [smallint] NOT NULL,
    [CodiceTipoAltroEvento] [smallint] NULL,
    [CodiceTipoClasseEvento] [tinyint] NOT NULL,
    [CodiceTipoCausaEvento] [tinyint] NULL,
    [CodiceTipoStatoSchedaEvento] [tinyint] NOT NULL,
    [DescrizioneEvento] [varchar](1000) NULL
 CONSTRAINT [PK_Eventi] PRIMARY KEY CLUSTERED 
(
    [CodiceEvento] ASC
)


CREATE TABLE [Evento].[EventiDettaglioBeneInteressato](
    [CodiceEvento] [varchar](20) NOT NULL,
    [PrgOrdinamento] [tinyint] NOT NULL,
    [CodiceTipoBeneRFI] [smallint] NOT NULL,
    [DescrizioneAltroBeneRFI] [varchar](255) NULL,
    [CodiceTipoRame] [varchar](25) NULL,
    [CodiceTipoUnitaMisura] [tinyint] NULL,
    [NumQuantita] [bigint] NULL,
 CONSTRAINT [PK_EventiDettaglioBeneInteressato] PRIMARY KEY CLUSTERED 
(
    [CodiceEvento] ASC,
    [PrgOrdinamento] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [Evento].[EventiDettaglioBeneInteressato]  WITH NOCHECK ADD  CONSTRAINT [FK_EventiDettaglioBeneInteressato_Eventi] FOREIGN KEY([CodiceEvento])
REFERENCES [Evento].[Eventi] ([CodiceEvento])
ON DELETE CASCADE
NOT FOR REPLICATION 
GO

Mapping Evento:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping assembly="Data.Model" namespace="Data.Model.Domain.Eventi" xmlns="urn:nhibernate-mapping-2.2">
  <class name="Eventi" table="Eventi" lazy="true" schema="Evento">
    <id name="CodiceEvento" column="CodiceEvento" />

    <many-to-one name="TipiEvento" update="false" insert="false">
      <column name="CodiceTipoEvento" sql-type="smallint" not-null="true" />
    </many-to-one>
    <property name="CodiceTipoEvento" >
      <column name="CodiceTipoEvento" sql-type="smallint" not-null="true" />
    </property>

    <many-to-one name="TipiAltroEvento" update="false" insert="false">
      <column name="CodiceTipoAltroEvento" sql-type="smallint" not-null="false" />
    </many-to-one>
    <property name="CodiceTipoAltroEvento" >
      <column name="CodiceTipoAltroEvento" sql-type="smallint" not-null="false" />
    </property>

    <many-to-one name="TipiClasseEvento" update="false" insert="false">
      <column name="CodiceTipoClasseEvento" sql-type="tinyint" not-null="true" />
    </many-to-one>
    <property name="CodiceTipoClasseEvento">
      <column name="CodiceTipoClasseEvento" sql-type="tinyint" not-null="true" />
    </property>

    <many-to-one name="TipiCausaEvento" update="false" insert="false">
      <column name="CodiceTipoCausaEvento" sql-type="tinyint" />
    </many-to-one>
    <property name="CodiceTipoCausaEvento" >
      <column name="CodiceTipoCausaEvento" sql-type="tinyint" />
    </property>

    <many-to-one name="TipiStatoSchedaEvento" update="false" insert="false">
      <column name="CodiceTipoStatoSchedaEvento" sql-type="tinyint" not-null="true" />
    </many-to-one>
    <property name="CodiceTipoStatoSchedaEvento">
      <column name="CodiceTipoStatoSchedaEvento" sql-type="tinyint" not-null="true" />
    </property>

    <many-to-one name="GestoriAsset" update="false" insert="false">
      <column name="CodiceGestoreAsset" sql-type="int" not-null="false" />
    </many-to-one>
    <property name="CodiceGestoreAsset">
      <column name="CodiceGestoreAsset" sql-type="int" not-null="false" />
    </property>

    <many-to-one name="TipiOraEvento" update="false" insert="false">
      <column name="CodiceTipoOraEvento" sql-type="tinyint" not-null="false" />
    </many-to-one>
    <property name="CodiceTipoOraEvento">
      <column name="CodiceTipoOraEvento" sql-type="tinyint" not-null="false" />
    </property>

    <many-to-one name="TipiAutoreEvento" update="false" insert="false">
      <column name="CodiceTipoAutoreEvento" sql-type="smallint" not-null="false" />
    </many-to-one>
    <property name="CodiceTipoAutoreEvento">
      <column name="CodiceTipoAutoreEvento" sql-type="smallint" not-null="false" />
    </property>

    <many-to-one name="TipiSegnalazioneEvento" update="false" insert="false">
      <column name="CodiceTipoSegnalazioneEvento" sql-type="smallint" not-null="false" />
    </many-to-one>
    <property name="CodiceTipoSegnalazioneEvento">
      <column name="CodiceTipoSegnalazioneEvento" sql-type="smallint" not-null="false" />
    </property>

    <property name="DescrizioneEvento">
      <column name="DescrizioneEvento" sql-type="varchar" not-null="false" />
    </property>

    <bag name="EventiDettaglio" table="EventiDettaglioBeneInteressato" schema="Evento" inverse="true" cascade="all,delete-orphan" >
      <key column="CodiceEvento" />
      <one-to-many class="EventiDettaglioBeneInteressato" />
    </bag> 

</class>
</hibernate-mapping>

Domain Model:

using System;
using System.Text;
using System.Collections.Generic;
using Data.Model.Attributes;

namespace Data.Model.Domain.Eventi
{

    [Serializable, Observable]
    public class Eventi : Entity, IEntity
    {

        public Eventi()
        {
            EventiDettaglio = new List<EventiDettaglioBeneInteressato>();
        }

        public virtual string CodiceEvento { get; set; }
        public virtual string DescrizioneEvento { get; set; }

        public virtual TipiEvento TipiEvento { get; set; }
        public virtual TipiAltroEvento TipiAltroEvento { get; set; }
        public virtual TipiClasseEvento TipiClasseEvento { get; set; }
        public virtual TipiCausaEvento TipiCausaEvento { get; set; }
        public virtual TipiStatoSchedaEvento TipiStatoSchedaEvento { get; set; }
        public virtual GestoriAsset GestoriAsset { get; set; }
        public virtual TipiOraEvento TipiOraEvento { get; set; }
        public virtual TipiAutoreEvento TipiAutoreEvento { get; set; }
        public virtual TipiSegnalazioneEvento TipiSegnalazioneEvento { get; set; }

        public virtual short CodiceTipoEvento { get; set; }
        public virtual short? CodiceTipoAltroEvento { get; set; }
        public virtual byte CodiceTipoClasseEvento { get; set; }
        public virtual byte? CodiceTipoCausaEvento { get; set; }
        public virtual byte CodiceTipoStatoSchedaEvento { get; set; }
        public virtual int? CodiceGestoreAsset { get; set; }
        public virtual byte? CodiceTipoOraEvento { get; set; }
        public virtual short? CodiceTipoAutoreEvento { get; set; }
        public virtual short? CodiceTipoSegnalazioneEvento { get; set; }       

        public virtual IList<EventiDettaglioBeneInteressato> EventiDettaglio { get; set; }


        #region NHibernate Composite Key Requirements
        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            var t = obj as Eventi;
            if (t == null) return false;
            if (CodiceEvento == t.CodiceEvento)
                return true;

            return false;
        }
        public override int GetHashCode()
        {
            int hash = GetType().GetHashCode();
            hash = (hash * 397) ^ CodiceEvento.GetHashCode();

            return hash;
        }
        #endregion
    }
}

Mapping EventiDettaglioBeneInteressato:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping assembly="Data.Model" namespace="Data.Model.Domain.Eventi" xmlns="urn:nhibernate-mapping-2.2">
  <class name="EventiDettaglioBeneInteressato" table="EventiDettaglioBeneInteressato" schema="Evento" lazy="true" >

    <composite-id>
      <key-property name="PrgOrdinamento" column="PrgOrdinamento" />
      <key-many-to-one class="Data.Model.Domain.Eventi.Eventi, Data.Model" name="Evento">
        <column name="CodiceEvento" />       
      </key-many-to-one> 
    </composite-id>   


    <property name="DescrizioneAltroBeneRFI">
      <column name="DescrizioneAltroBeneRFI" sql-type="varchar" not-null="false" />
    </property>
    <property name="NumQuantita">
      <column name="NumQuantita" sql-type="bigint" not-null="false" />
    </property>

    <many-to-one name="TipiBeneRFI" update="false" insert="false">
      <column name="CodiceTipoBeneRFI" sql-type="smallint" not-null="true" />
    </many-to-one>
    <property name="CodiceTipoBeneRFI">
      <column name="CodiceTipoBeneRFI" sql-type="smallint" not-null="true" />
    </property>

    <many-to-one name="TipiRame" update="false" insert="false">
      <column name="CodiceTipoRame" sql-type="varchar" not-null="false" />
    </many-to-one>
    <property name="CodiceTipoRame">
      <column name="CodiceTipoRame" sql-type="varchar" not-null="false" />
    </property>

    <many-to-one name="TipiUnitaMisura" update="false" insert="false">
      <column name="CodiceTipoUnitaMisura" sql-type="tinyint" not-null="false" />
    </many-to-one>
    <property name="CodiceTipoUnitaMisura">
      <column name="CodiceTipoUnitaMisura" sql-type="tinyint" not-null="false" />
    </property>

  </class>
</hibernate-mapping>

Domain Model:

using System;
using System.Text;
using System.Collections.Generic;
using NHibernate.Proxy;

namespace Data.Model.Domain.Eventi
{
    [Serializable]
    public class EventiDettaglioBeneInteressato : Entity, IEntity
    {
        public virtual Eventi Evento { get; set; }
        public virtual byte PrgOrdinamento { get; set; }

        public virtual TipiBeneRFI TipiBeneRFI { get; set; }
        public virtual TipiRame TipiRame { get; set; }
        public virtual TipiUnitaMisura TipiUnitaMisura { get; set; }


        public virtual short CodiceTipoBeneRFI { get; set; }
        public virtual string CodiceTipoRame { get; set; }
        public virtual byte? CodiceTipoUnitaMisura { get; set; }


        public virtual string DescrizioneAltroBeneRFI { get; set; }
        public virtual long? NumQuantita { get; set; }

        #region NHibernate Composite Key Requirements
        public override int GetHashCode()
        {
            unchecked
            {
                int hash = GetType().GetHashCode();
                hash = (hash * 397) ^ PrgOrdinamento.GetHashCode();
                hash = (hash * 397) ^ (Evento == null ? 0.GetHashCode() : Evento.GetHashCode());
                return hash;
            }
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as EventiDettaglioBeneInteressato);
        }

        public virtual bool Equals(EventiDettaglioBeneInteressato other)
        {
            if (other == null)
            {
                return false;
            }

            if (ReferenceEquals(other, this))
            {
                return true;
            }

            var otherType = NHibernateProxyHelper.GetClassWithoutInitializingProxy(other);
            var thisType = NHibernateProxyHelper.GetClassWithoutInitializingProxy(this);
            if (!otherType.Equals(thisType))
            {
                return false;
            }

            bool otherIsTransient = Equals(other.Evento, null) && Equals(other.PrgOrdinamento, 0);
            bool thisIsTransient = Equals(Evento, null) && Equals(PrgOrdinamento, 0);
            if (otherIsTransient || thisIsTransient)
                return false;

            return Equals(other, this);
        }

        private bool Equals(EventiDettaglioBeneInteressato a, EventiDettaglioBeneInteressato b)
        {
            if (a.Evento == b.Evento
                && a.PrgOrdinamento == b.PrgOrdinamento)
                return true;

            return false;
        }
        #endregion
    }
}

Test:

    [TestMethod]
    public void EventoTest_Add_Child_BeniInteressati()
    {
        var codiceEvento = "20140630_013325_BA";
        using (var session = IoC.Container.Resolve<ISessionFactory>().OpenSession())
        {
            var evento = session.Get<Data.Model.Domain.Eventi.Eventi>(codiceEvento);

            Assert.AreEqual(evento.EventiDettaglio.Count, 0);

            var beneInteressato = new Data.Model.Domain.Eventi.EventiDettaglioBeneInteressato();
            beneInteressato.PrgOrdinamento = 1;
            beneInteressato.Evento = evento;
            beneInteressato.CodiceTipoBeneRFI = 1;

            evento.EventiDettaglio.Add(beneInteressato);

            var beneInteressato2 = new Data.Model.Domain.Eventi.EventiDettaglioBeneInteressato();
            beneInteressato.PrgOrdinamento = 2;
            beneInteressato.Evento = evento;
            beneInteressato.CodiceTipoBeneRFI = 2;

            evento.EventiDettaglio.Add(beneInteressato2);

            session.SaveOrUpdate(evento);
            session.Flush();
        }
    }

Error:

Message:
Cannot insert the value NULL into column 'CodiceEvento', table 'Evento.EventiDettaglioBeneInteressato'; column does not allow nulls. INSERT fails.
The statement has been terminated.

StackTrace:
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.SqlClient.SqlCommand.ExecuteBatchRPCCommand()
   at System.Data.SqlClient.SqlCommandSet.ExecuteNonQuery()
   at NHibernate.AdoNet.SqlClientSqlCommandSet.ExecuteNonQuery()
   at NHibernate.AdoNet.SqlClientBatchingBatcher.DoExecuteBatch(IDbCommand ps)

LOG:

18:15:25.695 [10] WARN  NHibernate.Engine.ForeignKeys - Unable to determine if Data.Model.Domain.Eventi.EventiDettaglioBeneInteressato with assigned identifier Data.Model.Domain.Eventi.EventiDettaglioBeneInteressato is transient or detached; querying the database. Use explicit Save() or Update() in session to prevent this.
18:15:25.716 [10] WARN  NHibernate.Engine.ForeignKeys - Unable to determine if Data.Model.Domain.Eventi.EventiDettaglioBeneInteressato with assigned identifier Data.Model.Domain.Eventi.EventiDettaglioBeneInteressato is transient or detached; querying the database. Use explicit Save() or Update() in session to prevent this.
18:15:25.960 [10] WARN  NHibernate.Util.ADOExceptionReporter - System.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'CodiceEvento', table 'Evento.EventiDettaglioBeneInteressato'; column does not allow nulls. INSERT fails.
The statement has been terminated.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.SqlClient.SqlCommand.ExecuteBatchRPCCommand()
   at System.Data.SqlClient.SqlCommandSet.ExecuteNonQuery()
   at NHibernate.AdoNet.SqlClientSqlCommandSet.ExecuteNonQuery()
   at NHibernate.AdoNet.SqlClientBatchingBatcher.DoExecuteBatch(IDbCommand ps)
ClientConnectionId:b0edabca-f2b9-4cc5-95b8-120653b5dc31
18:15:26.012 [10] ERROR NHibernate.Util.ADOExceptionReporter - Cannot insert the value NULL into column 'CodiceEvento', table 'Evento.EventiDettaglioBeneInteressato'; column does not allow nulls. INSERT fails.
The statement has been terminated.
18:15:26.047 [10] ERROR NHibernate.Event.Default.AbstractFlushingEventListener - Could not synchronize database state with session
NHibernate.Exceptions.GenericADOException: could not execute batch command.[SQL: SQL not available] ---> System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'CodiceEvento', table 'Evento.EventiDettaglioBeneInteressato'; column does not allow nulls. INSERT fails.
The statement has been terminated.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at System.Data.SqlClient.SqlCommand.ExecuteBatchRPCCommand()
   at System.Data.SqlClient.SqlCommandSet.ExecuteNonQuery()
   at NHibernate.AdoNet.SqlClientSqlCommandSet.ExecuteNonQuery()
   at NHibernate.AdoNet.SqlClientBatchingBatcher.DoExecuteBatch(IDbCommand ps)
   --- End of inner exception stack trace ---
   at NHibernate.AdoNet.SqlClientBatchingBatcher.DoExecuteBatch(IDbCommand ps)
   at NHibernate.AdoNet.AbstractBatcher.ExecuteBatchWithTiming(IDbCommand ps)
   at NHibernate.AdoNet.AbstractBatcher.ExecuteBatch()
   at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
   at NHibernate.Engine.ActionQueue.ExecuteActions()
   at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)

Thanks. Salvatore

First of all I saw some "text" errors in the code, but I think it's a simple writing mistake ;-) Proceding with order with your question, the answer points are:

  1. Working with collections of child objects, and with cascades, requires first of all the usage of Inverse as you done, because this tells nhibernate to first save the root object and then assign the root-saved-object-key to the each of the childs to be saved. But this is not the only thing to do to makes cascades working correctly.
  2. Working with two-side reference collections of child objects, like yours, when you have a root with child and each child having a reference to his root, it's not so good to working directly on the list of child inside the root by adding items to that, this because when you add a child object, the root has the responsibility to set each child root-reference. To do that the steps to involve into are:
    • add a private field of IList<EventiDettaglioBeneInteressato> inside the Eventi class, and then in the constructor initialize it as a new empty collection of them
    • Inside the Eventi class change the EventiDettaglio property from IList<EventiDettaglioBeneInteressato> to IEnumerable<EventiDettaglioBeneInteressato>
    • Inside the Eventi class change the property from being an auto-property to a readonly property public virtual IEnumerable<EventiDettaglioBeneInteressato> EventiDettaglio { get { return _eventiDettaglio; } } public virtual IEnumerable<EventiDettaglioBeneInteressato> EventiDettaglio { get { return _eventiDettaglio; } }
    • Inside the Eventi class mapping change the cascade property of the bag EventiDettaglio to cascade="all-delete-orphan" , then specify nhibernate that the bag will be accessed by backing field instead of property, this is done by adding access="nosetter.camelcase-underscore"
    • After all, it's needed to create an Add method to the Eventi class to add child EventiDettaglioBeneInteressato to the class, inside that method will be set the reference father-child as you want to do: public virtual void Add(EventiDettaglioBeneInteressato dettaglioBeneInteressato) { dettaglioBeneInteressato.Evento = this; _eventiDettaglio.Add(dettaglioBeneInteressato); } public virtual void Add(EventiDettaglioBeneInteressato dettaglioBeneInteressato) { dettaglioBeneInteressato.Evento = this; _eventiDettaglio.Add(dettaglioBeneInteressato); }

These are all the steps to make working as you desire, so my modified test is this (I assume using NUnit as testframework, and FluentAssertions as helper framework for making assertions, but easily you can change the method to use MSTest):

 [Test]
    public void TestMethod()
    {
        var session = SessionFactory.OpenSession();
        var codiceEvento = "20140630_013325_BA";

        //Step1. Create a new Eventi to be saved with the given code

        using (var trx = session.BeginTransaction())
        {
            var eventi = new Eventi
            {
                CodiceEvento = codiceEvento
            };
            session.SaveOrUpdate(eventi);
            trx.Commit();
        }

        //Step2. Clear the session so the Eventi object have to be loaded again
        session.Clear();
        using (var trx = session.BeginTransaction())
        {
            var evento = session.Get<Eventi>(codiceEvento);

            //assert the object has no child yet
            evento.EventiDettaglio.Count().Should().Be(0);

            //Step3. Add objects without specify the relationship
            evento.Add(new EventiDettaglioBeneInteressato
            {
                PrgOrdinamento = 1,
                CodiceTipoBeneRFI = 1
            });

            evento.Add(new EventiDettaglioBeneInteressato
            {
                PrgOrdinamento = 2,
                CodiceTipoBeneRFI = 2
            });
            evento.Add(new EventiDettaglioBeneInteressato
            {
                PrgOrdinamento = 3,
                CodiceTipoBeneRFI = 3
            });
            evento.Add(new EventiDettaglioBeneInteressato
            {
                PrgOrdinamento = 4,
                CodiceTipoBeneRFI = 4
            });

            //Step4. save the root and then check if all the collection is saved
            session.SaveOrUpdate(evento);
            trx.Commit();
        }

        //Step5. clear again the session to make sure all the things are loaded well from db
        session.Clear();
        using (var trx = session.BeginTransaction())
        {
            var evento = session.Get<Eventi>(codiceEvento);

            //Step6. load the root and then assert the child collection has 4 elements
            evento.EventiDettaglio.Count().Should().Be(4);

            //Step7. delete the root object to repeat the test again and test the cascade
            session.Delete(evento);
            trx.Commit();
        }

        //Step8. check there's no child object for that codiceEvento. the cascade is working!
        using (var trx = session.BeginTransaction())
        {
            session.Query<EventiDettaglioBeneInteressato>()
                .Count(it => it.Evento.CodiceEvento == codiceEvento)
                .Should().Be(0);

            trx.Commit();
        }

    }

Just to be useful, this are the modified class and mapping (modified to working outside your enbvironment):

Eventi class

    [Serializable]
public class Eventi
{
    private IList<EventiDettaglioBeneInteressato> _eventiDettaglio; 
    public Eventi()
    {
        _eventiDettaglio = new List<EventiDettaglioBeneInteressato>();
    }

    public virtual string CodiceEvento { get; set; }
    public virtual string DescrizioneEvento { get; set; }

    public virtual short CodiceTipoEvento { get; set; }
    public virtual short? CodiceTipoAltroEvento { get; set; }
    public virtual byte CodiceTipoClasseEvento { get; set; }
    public virtual byte? CodiceTipoCausaEvento { get; set; }
    public virtual byte CodiceTipoStatoSchedaEvento { get; set; }
    public virtual int? CodiceGestoreAsset { get; set; }
    public virtual byte? CodiceTipoOraEvento { get; set; }
    public virtual short? CodiceTipoAutoreEvento { get; set; }
    public virtual short? CodiceTipoSegnalazioneEvento { get; set; }       

    public virtual IEnumerable<EventiDettaglioBeneInteressato> EventiDettaglio { get { return _eventiDettaglio; } }

    public virtual void Add(EventiDettaglioBeneInteressato dettaglioBeneInteressato)
    {
        dettaglioBeneInteressato.Evento = this;
        _eventiDettaglio.Add(dettaglioBeneInteressato);
    }

    #region NHibernate Composite Key Requirements
    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        var t = obj as Eventi;
        if (t == null) return false;
        if (CodiceEvento == t.CodiceEvento)
            return true;

        return false;
    }
    public override int GetHashCode()
    {
        int hash = GetType().GetHashCode();
        hash = (hash * 397) ^ CodiceEvento.GetHashCode();

        return hash;
    }
    #endregion
}

Eventi class mapping

<hibernate-mapping assembly="MyOverFlow.Tests" namespace="MyOverFlow.Tests.NHibernate.Domain" xmlns="urn:nhibernate-mapping-2.2">
  <class name="Eventi" table="Eventi" lazy="true" schema="Evento">
    <id name="CodiceEvento" column="CodiceEvento" />

    <property name="CodiceTipoEvento" >
      <column name="CodiceTipoEvento" sql-type="smallint" not-null="true" />
    </property>

    <property name="CodiceTipoAltroEvento" >
      <column name="CodiceTipoAltroEvento" sql-type="smallint" not-null="false" />
    </property>

    <property name="CodiceTipoClasseEvento">
      <column name="CodiceTipoClasseEvento" sql-type="tinyint" not-null="true" />
    </property>

    <property name="CodiceTipoCausaEvento" >
      <column name="CodiceTipoCausaEvento" sql-type="tinyint" />
    </property>

    <property name="CodiceTipoStatoSchedaEvento">
      <column name="CodiceTipoStatoSchedaEvento" sql-type="tinyint" not-null="true" />
    </property>

    <property name="CodiceGestoreAsset">
      <column name="CodiceGestoreAsset" sql-type="int" not-null="false" />
    </property>

    <property name="CodiceTipoOraEvento">
      <column name="CodiceTipoOraEvento" sql-type="tinyint" not-null="false" />
    </property>

    <property name="CodiceTipoAutoreEvento">
      <column name="CodiceTipoAutoreEvento" sql-type="smallint" not-null="false" />
    </property>

    <property name="CodiceTipoSegnalazioneEvento">
      <column name="CodiceTipoSegnalazioneEvento" sql-type="smallint" not-null="false" />
    </property>

    <property name="DescrizioneEvento">
      <column name="DescrizioneEvento" sql-type="varchar" not-null="false" />
    </property>

    <bag name="EventiDettaglio" table="EventiDettaglioBeneInteressato"
         access="nosetter.camelcase-underscore"
         schema="Evento" inverse="true" cascade="all-delete-orphan" >
      <key column="CodiceEvento" />
      <one-to-many class="EventiDettaglioBeneInteressato" />
    </bag>

  </class>
</hibernate-mapping>

Hope this help!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM