views:

487

answers:

3

Hi

I keep getting the following error:

Cannot access a disposed object. Object name: 'AdoTransaction'.

The setup follows the example given at http://trason.net/journal/2009/10/7/bootstrapping-nhibernate-with-structuremap.html

here is the IUnitOfWork class (exactly the same as the one in the link):

public class UnitOfWork : IUnitOfWork
{
    private readonly ISessionFactory _sessionFactory;
    private readonly ITransaction _transaction;

    public UnitOfWork(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
        CurrentSession = _sessionFactory.OpenSession();
        _transaction = CurrentSession.BeginTransaction();
    }

    public ISession CurrentSession { get; private set; }

    public void Dispose()
    {
        CurrentSession.Close();
        CurrentSession = null;
    }

    public void Commit()
    {
        _transaction.Commit();
    }

}

here is the NHibernateModule (again exactly the same!):

public class NHibernateModule : IHttpModule, IDisposable
{
    private IUnitOfWork _unitOfWork;

    public void Init(HttpApplication context)
    {
        context.BeginRequest += ContextBeginRequest;
        context.EndRequest += ContextEndRequest;
    }

    private void ContextBeginRequest(object sender, EventArgs e)
    {
        _unitOfWork = ObjectFactory.GetInstance<IUnitOfWork>();
    }

    private void ContextEndRequest(object sender, EventArgs e)
    {
        Dispose();
    }

    public void Dispose()
    {
        if (_unitOfWork == null) return;
        _unitOfWork.Commit();
        _unitOfWork.Dispose();
    }
}

here is my repo:

 public class Repository<T> : IRepository<T>
{
    public readonly IUnitOfWork _uow;

    public Repository(IUnitOfWork uow)
    {
        _uow = uow;
    }

    public Repository()
    {

    }

    #region IRepository<T> Members

    public IList<T> GetAll()
    {
        using (var session = _uow.CurrentSession)
        {
            return session.CreateCriteria(typeof (T)).List<T>();
        }
    }

    public IList<T> FindAll<T>(IList<Expression<Func<T, bool>>> criteria)
    {
        var session = _uow.CurrentSession;

        var query = from item in session.SessionFactory.OpenSession().Query<T>()
                              select item;
        foreach (var criterion in criteria)
        {
            query = query.Where(criterion);
        }
        return query.ToList();
    }

    public T FindFirst<T>(IList<Expression<Func<T, bool>>> criteria)
    {

        var col = FindAll(criteria);

        if (col.Count > 0)
        {
            return col.First();
        }
        else
        {
            return default(T);
        }
    }

    public T Get(int id)
    {
        using (var session = _uow.CurrentSession)
        {
            return session.Get<T>(id);
        }
    }

    public void Save(T entity)
    {
        using (var session = _uow.CurrentSession)
        {
            session.Save(entity);
        }
    }

    public void Update(T entity)
    {
        using (var session = _uow.CurrentSession)
        {
            session.Update(entity);
            session.Flush();
        }
    }

    #endregion
}
}

here is my bootstrapper:

 public class BootStrapper : IBootstrapper
{
    private static bool _hasStarted;

    public virtual void BootstrapStructureMap()
    {
        ObjectFactory.Initialize(x =>
        {
            x.Scan(s =>
            {
                s.TheCallingAssembly();
                s.AssemblyContainingType<User>();
                s.AssemblyContainingType<UserRepository>();
                s.AssemblyContainingType<NHibernateRegistry>();
                s.LookForRegistries();
            });

            // Repositories
            x.For<WmcStar.Data.IUserRepository>()
                .CacheBy(InstanceScope.HttpContext)
                .TheDefault.Is.OfConcreteType<UserRepository>();

            x.For<IDatabaseBuilder>().TheDefaultIsConcreteType<DatabaseBuilder>(); 

        });
    }

    public static void Restart()
    {
        if (_hasStarted)
        {
            ObjectFactory.ResetDefaults();
        }
        else
        {
            Bootstrap();
            _hasStarted = true;
        }
    }

    public static void Bootstrap()
    {
        new BootStrapper().BootstrapStructureMap();
    }

}

here is my NHibernateRegistry:

public class NHibernateRegistry : Registry
{
    public NHibernateRegistry()
    {
        var cfg = new Configuration()
             .SetProperty(NHibernate.Cfg.Environment.ReleaseConnections, "on_close")
             .SetProperty(NHibernate.Cfg.Environment.Dialect, typeof(NHibernate.Dialect.MsSql2005Dialect).AssemblyQualifiedName)
             .SetProperty(NHibernate.Cfg.Environment.ConnectionDriver, typeof(NHibernate.Driver.SqlClientDriver).AssemblyQualifiedName)
             .SetProperty(NHibernate.Cfg.Environment.ConnectionString, @"my connstring")
             .SetProperty(NHibernate.Cfg.Environment.ProxyFactoryFactoryClass, typeof(ProxyFactoryFactory).AssemblyQualifiedName)
             .AddAssembly(typeof(User).Assembly);

        var sessionFactory = cfg.BuildSessionFactory();

        For<Configuration>().Singleton().Use(cfg);

        For<ISessionFactory>().Singleton().Use(sessionFactory);


        For<ISession>().HybridHttpOrThreadLocalScoped()
            .Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession());

        For<IUnitOfWork>().HybridHttpOrThreadLocalScoped()
            .Use<UnitOfWork>();

        For<IDatabaseBuilder>().Use<DatabaseBuilder>();
        SetAllProperties(x => x.OfType<IUnitOfWork>());
    }
}

and finaly here is my global.asax:

    public class Global : System.Web.HttpApplication
{

    protected void Application_Start(object sender, EventArgs e)
    {
        BootStrapper.Bootstrap();
        new SchemaExport(ObjectFactory.GetInstance<Configuration>()).Execute(false, true, false);
        ObjectFactory.GetInstance<IDatabaseBuilder>().RebuildDatabase();

        AutoMapper.Mapper.CreateMap<WmcStar.Model.User, WmcStar.Data.Dto.User>();
    }
}

Anyone got any clues as to what would cause this?

w://

A: 

Perhaps it has nothing to do with the combination of StructureMap and NHibernate, and everything to do with your code? I'm guessing without seeing code, but most likely you're disposing of a transaction and then attempting to access it afterwards.

HTH,
Kent

Kent Boogaart
Sorry dude - i did actually say that there is a problem with nhibernate/structuremap setup <--- SETUP!!
cvista
+1  A: 

I don't know what the exact problem is that causes the exception, but I have found some potential issues:

  1. Commit the unit of work at the end of the request, and roll it back when commit fails.
  2. Rollback the unit of work when an exception is thrown during the request.
  3. Don't call dispose at the end of the request, it's abusing the IDisposable of the class.
  4. Dispose the unit of work at the end of the request, when not already disposed.
  5. Do not dispose the session in every method in your repository.
  6. Dispose the session in the unit of work dispose method.
  7. You don't have to explicitly close the session. disposing is enough.
  8. Dispose the transaction before the session in the dispose method.
  9. Use checks in the dispose of the unit of work method to prevent memory leaks and make the dispose method garbage collector proof.

After typing this list, I think the exception is caused by the using blocks in the repository.

If you want an explanation for one of these things, please ask me in the comments.

Paco
Heythanks so much - i've done most of what you said but the in the list:Use checks in the dispose of the unit of work method to prevent memory leaks and make the dispose method garbage collector proof.can you expand on this?thankyou for an excellent answer :)w://
cvista
http://msdn.microsoft.com/en-us/library/ms244737%28VS.80%29.aspx
Paco
+1  A: 

I ran into this issue when using StructureMap and NHibernate. Basically, I have structuremap map a named object (eg. CurrentUser). In that mapping, it uses an NHibernate session to retrieve the current user from the db.

Then in my code somewhere, I was doing something like:

using (var transaction = _session.BeginTransaction())
{
  var user = ObjectFactory.GetNamedInstance<User>("CurrentUser");
  var myObjects = _session.QueryOver<myObject>().Where(x => x.User == CurrentUser).Future();
  transaction.Commit();
}

The error is thrown because the transaction is disposed as part of the current user operation. So when program execution reaches the transaction.Commit() line, there is no transaction to commit. My solution was to just move the user line outside of the transaction statement:

var user = ObjectFactory.GetNamedInstance<User>("CurrentUser");
using (var transaction = _session.BeginTransaction())
{
  var myObjects = _session.QueryOver<MyObject>().Where(x => x.User == CurrentUser).Future();
  transaction.Commit();
}

This will allow the transaction around the current user retrieval to be independent of the transaction for MyObject.

Jim Geurts