1

I am facing an unusual behaviour with the Repository and Transactions and its making me mad.

I have two simple POCO classes Action and Version as follows. There is a one to many relationship from Action->Version.

public class Action : Entity
{
    public virtual string Name
    {
        get;
        set;
    }

    public virtual IList<Version> Versions
    {
        get;
        protected set;
    }

    public Action()
    {
        Versions = new List<Version>();
    }

    public virtual void AddVersion( Version version )
    {
        version.Action = this;
        Versions.Add( version );
    }
}

public class Version : Entity
{

    public virtual int Number
    {
        get;
        set;
    }

    public virtual Action Action
    {
        get;
        set;
    }

    public Version()
    {
    }
}

Now I have a code segment as follows:

    var actionRep = new Repository<Action>();
    var versionRep = new Repository<Version>();

    var act1 = new Action( );
    actionRep .SaveOrUpdate( act1 );

    using (versionRep .DbContext.BeginTransaction())
    {

        var v1 = new Version();
        act1.AddVersion( v1 );
        //versionRep .SaveOrUpdate( v1 );
        versionRep .DbContext.CommitTransaction();
    }

In the above code I am just creating Action and Version repositories. First I persist an Action object using Action Repository. Then I begin a transaction using Version repository , create a new version, set references with the Action, and Commit the transaction without actually calling Version Repository.

The result is a bit strange. The version object gets persisted even though I have not called SaveOrUpdate method on version repository. If I comment out the line act1.AddVersion( v1 ); within the transaction, then the Version does not get persisted.

After a bit of struggle I tested the same scenario using NHibernate directly instead of using Sharp Architecture repositories using same fluent mapping/configuration (AutoPersistenceModelGenerator.Generate()). And The result is as expected. The version object does not get persisted. Here is the code

    var sessionFactory = CreateSessionFactory();
    _act1 = new Action();

    using( var session = sessionFactory.OpenSession() )
    {
        session.SaveOrUpdate( _act1 );
    }

    using( var session = sessionFactory.OpenSession() )
    {
        using (var transaction = session.BeginTransaction())
        {
            _v1 = new Version();
            _act1.AddVersion( _v1 );
            //session.SaveOrUpdate( _act1 );
            transaction.Commit();
        }
    }

The CreateSessionFactory() method is as follows. Nothing complicated

        private const string _dbFilename = "nhib_auditing.db";
        private static ISessionFactory CreateSessionFactory()
        {
            return Fluently.Configure()
                .Database( SQLiteConfiguration.Standard
                            .UsingFile( _dbFilename )
                            .ShowSql())
                .Mappings( m => m.AutoMappings.Add( new
AutoPersistenceModelGenerator().Generate() ) )
                .ExposeConfiguration( BuildSchema )
                .BuildSessionFactory();
        }

Now If sombody could please let me know why I am having this behaviour. Its making me mad.

Just so that you know I have not overridden mapping for Action and Version either.

Awaiting Nabeel

Jamie Ide
  • 48,427
  • 16
  • 81
  • 117
nabeelfarid
  • 4,156
  • 5
  • 42
  • 60

2 Answers2

4

This is the expected behavior if you have cascading operations declared in the mapping. When you called SaveOrUpdate on act1 you made the transient object persistent; that is, NHibernate is tracking it and will save it when the session is flushed. Another way to make an object persistent is to associate it with a persistent object, as you did when you called act1.AddVersion( v1 );. The session was flushed when the transaction was committed so v1 was saved. This article explains the details of working with persistent objects.

Jamie Ide
  • 48,427
  • 16
  • 81
  • 117
0

By the way, Here are my two tests one with SharpRepositories which is failing and the ther with NHibernate Directly which is passing.

    [TestFixture]
    public class RepositoryTests : RepositoryTestsBase
    {
        protected override void LoadTestData()
        {
        }

        [Test]
        public void TestUsingSharpRespositories
        {
            var aRep = new Repository<Action>();
            var vRep = new Repository<Version>();

            _act1 = new Action();
            aRep.SaveOrUpdate( _act1 );

            using( vRep.DbContext.BeginTransaction() )
            {
                _v1 = new Version();
                _act1.AddVersion( _v1 );
                //vRep.SaveOrUpdate(_v1);
                vRep.DbContext.CommitTransaction();
            }
            _v1.IsTransient().ShouldBeTrue();
        }

        [Test]
        public void TestUsingNHibernateSession
        {
            var sessionFactory = CreateSessionFactory();
            _act1 = new Action();

            using( var session = sessionFactory.OpenSession() )
            {
                session.SaveOrUpdate( _act1 );
            }

            using( var session = sessionFactory.OpenSession() )
            {
                using (var transaction = session.BeginTransaction())
                {
                    _v1 = new Version();
                    _act1.AddVersion( _v1 );

                    transaction.Commit();
                }
            }

            _v1.IsTransient().ShouldBeTrue();
        }

        private const string _dbFilename = "nhib_db.db";
        private static ISessionFactory CreateSessionFactory()
        {
            return Fluently.Configure()
                .Database( SQLiteConfiguration.Standard
                            .UsingFile( _dbFilename )
                            .ShowSql())
                .Mappings( m => m.AutoMappings.Add( new
AutoPersistenceModelGenerator().Generate() ) )
                .ExposeConfiguration( BuildSchema )
                .BuildSessionFactory();
        }

        private static void BuildSchema( Configuration config )
        {
            if( File.Exists( _dbFilename ) )
                File.Delete( _dbFilename );
            new SchemaExport( config ).Create( false, true );
        }
    }

I am new to Fluent NHibernate and I have never used NHibernate directly with xml mapping.

As far as mapping is concerned, I am using vanilla automapping configuration that is setup when you create a new Sharp Architecture project in visual studio. I have a convention for HasMany as follows:

public class HasManyConvention : IHasManyConvention
    {
        public void
Apply( FluentNHibernate.Conventions.Instances.IOneToManyCollectionInstance
instance )
        {
            instance.Key.Column( instance.EntityType.Name + "Fk" );
            instance.Inverse();
            instance.Cascade.All();
        }
    } 

I updated my TestUsingNHibernateSession test as follows to test how it behaves if I manipulate both Action and Version in a single session. And guess what, the Version object gets saved, even though there is nothing between the Transaction's begin and commit.

    [Test]
    public void TestUsingNHibernateSession
    {
        var sessionFactory = CreateSessionFactory();
        _act1 = new Action();

        using( var session = sessionFactory.OpenSession() )
        {
            session.SaveOrUpdate( _act1 );
        //}

        //using( var session = sessionFactory.OpenSession() )
        //{
            _v1 = new Version();
            _act1.AddVersion( _v1 );
            using (var transaction = session.BeginTransaction())
            {
                transaction.Commit();
            }
        }

        _v1.IsTransient().ShouldBeTrue();
    }

So my conclusion is that its all to do with the Session. If an object A has been created or retrieved in a session and then later on if a new transaction begins within that session, then as soon as that transaction is commited, all the transient or dirty objects associated with object A gets saved as well, no matter wether objects gets created within or outside of Begin and Commit.

Could anyone please let me know if they agree with my understanding? And I am also assuming that Sharp architecture uses a single nhibernate Session throughtout each web request?

Awaiting Nabeel

nabeelfarid
  • 4,156
  • 5
  • 42
  • 60
  • You should edit your question to include this rather than posting it as an answer –  Jun 22 '10 at 22:08
  • Sorry about that Isaac. I actually posted it on S#arp Architecture google mailing list initially, and I kinda copy pasted from there – nabeelfarid Jun 23 '10 at 10:37