views:

45

answers:

2

Hi guys,

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

A: 

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
You should edit your question to include this rather than posting it as an answer
Isaac Cambron
Sorry about that Isaac. I actually posted it on S#arp Architecture google mailing list initially, and I kinda copy pasted from there
nabeelfarid
+2  A: 

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