Click here to Skip to main content
15,885,885 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hi all,
I have this exception "illegal attempt to associate a collection with two open sessions", it raises every time I save entity contains collection of children.
I google it. I found that I opened two or more sessions when calling save, but I'm sure that I'm using only one session.
Where I did wrong? How can I solve this problemn?
Note: I'm using MVC4, and fluent NHibernate.

Entities:
C#
public class Employee : EntityBase<int>
    {
        public Employee()
            : base()
        {
            Phones = new List<Phone>();
        }

        public Employee(int id) : this() { Id = id; }
        [Browsable(false)]
        public override ApprovalBase Approval
        {
            get;
            set;
        }

        public virtual string Name { get; set; }
        public virtual string Job { get; set; }

        [Browsable(false)]
        public virtual IList<Phone> Phones { get; set; }
    }
public class Phone:EntityBase<int>
    {
        public Phone()
            : base()
        {
        }
        public Phone(int id) : this() { Id = id; }
        public override ApprovalBase Approval
        {
            get;
            set;
        }

        public virtual string PhoneNumber { get; set; }
        public virtual string PhoneType { get; set; }
        public virtual int EmployeeId { get; set; }

        public virtual Employee Employee { get; set; }
    }


Mapping:
C#
public sealed class EmployeeMap : ClassMap<Employee>
    {
        public EmployeeMap()
        {
            Table("dbo.Employee");

            Id(x => x.Id).Column("EmployeeId");
            Map(x => x.Name);
            Map(x => x.Job);
            HasMany(x => x.Phones).KeyColumn("EmployeeId").Table("dbo.Phone").Cascade.All().Inverse();
        }
    }
public sealed class PhoneMap : ClassMap<Phone>
    {
        public PhoneMap()
        {
            Table("dbo.Phone");
            Id(x => x.Id).Column("PhoneId");
            Map(x => x.PhoneNumber);
            Map(x => x.PhoneType);
            Map(x => x.EmployeeId);
            References(x => x.Employee).Column("EmployeeId")
                .Not.Update()
                .Not.Insert();
        }
    }


Repository:
C#
public interface IPersistor<TEntity, TIdentity>
    {
        TEntity Get(TIdentity id);
        void Save(TEntity entity);
        void Delete(TIdentity id);
        IQueryable<TEntity> Query();
    }
    public interface IRepository<TEntity, TIdentity>
        where TEntity : EntityBase<TIdentity>
        where TIdentity : IComparable
    {
        IList<TEntity> GetAll();
        TEntity Get(TIdentity id);
        IList<String> Save(TEntity entity);
        void Delete(TIdentity id);
        // hidden // IQueryable<TEntity> Query();
    }

    public abstract class RepositoryBase<TEntity, TIdentity>
        : IRepository<TEntity, TIdentity>
        where TEntity : EntityBase<TIdentity>
        where TIdentity : IComparable
    {
        private readonly IPersistor<TEntity, TIdentity> persistor;
        public IPersistor<TEntity, TIdentity> Persistor { get { return persistor; } }

        private readonly IFinder<TEntity, TIdentity> finder;
        public IFinder<TEntity, TIdentity> Finder { get { return finder; } }

        private RepositoryBase() { }

        public RepositoryBase(
            IPersistor<TEntity, TIdentity> persistor,
            IFinder<TEntity, TIdentity> finder)
        {
            this.persistor = persistor;
            this.finder = finder;
            this.finder.DataSource = Query();
        }

        // Get entity by ID
        public virtual TEntity Get(TIdentity id)
        {
            return persistor.Get(id);
        }

        /// <summary>
        /// Validate and Save the entity. If the validation failed, will not save the entity,
        /// but returns back list of error messages.
        /// </summary>
        public virtual IList<String> Save(TEntity entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            IList<String> errors = entity.Validate();

            if (errors.Count == 0)
            {
                persistor.Save(entity);
            }

            return errors;
        }

        // Delete entity from persistance repository
        public virtual void Delete(TIdentity id)
        {
            persistor.Delete(id);
        }

        /// Gets IQueryable which we use from the concrete
        /// implementation of repository to implement our 
        /// query methods (FindBy).
        protected IQueryable<TEntity> Query()
        {
            return persistor.Query();
        }

        public IList<TEntity> GetAll()
        {
            return persistor.Query().ToList();
        }
    }
//Employee and Phone Repository inherits from this class


Persistor
C#
public abstract class PersistorBase<TEntity, TIdentity>
        : IPersistor<TEntity, TIdentity>
    {
        protected ISession Session { get; set; }

        public PersistorBase(ISession session)
        {
            Session = session;
        }

        public TEntity Get(TIdentity id)
        {
            return (TEntity)Session.Get(typeof(TEntity), id);
        }

        public void Save(TEntity entity)
        {
            try
            {
                Session.SaveOrUpdate(entity);
                NhUnitOfWork.Current.Commit();
            }
            catch
            {
                NhUnitOfWork.Current.Rollback();
            }
        }

        public void Delete(TIdentity id)
        {
            try
            {
                Session.Delete(Session.Load<TEntity>(id));
                NhUnitOfWork.Current.Commit();
            }
            catch
            {
                NhUnitOfWork.Current.Rollback();
            }
        }

        public IQueryable<TEntity> Query()
        {
            var qry = from t in Session.Query<TEntity>()
                      select t;

            return qry.AsQueryable();
        }
    }
//Employee and phone persistors inherits from this class


Session factory:
C#
//I have class that inherits from this class, and when the constructor called from controller constructor, the Init() called.
public abstract class BaseDatabase : IDisposable
    {
        protected static Configuration configuration;
        protected static ISessionFactory sessionFactory;

        public ISession Session { get; protected set; }

        /// Does initialization, run from the constructors of the inherited classes.
        protected void Init()
        {
            sessionFactory = CreateSessionFactory();
            //Session = sessionFactory.OpenSession();
            NhUnitOfWork.Current = new NhUnitOfWork(sessionFactory);
            NhUnitOfWork.Current.BeginTransaction();
            Session = NhUnitOfWork.Current.Session;
            //BuildSchema(Session);
        }

        protected abstract ISessionFactory CreateSessionFactory();

        //private static void BuildSchema(ISession session)
        //{
        //    SchemaExport schemaExport = new SchemaExport(configuration);
        //    schemaExport.Execute(true, true, false, session.Connection, null);
        //}

        public void Dispose()
        {
            Session.Dispose();
        }

    }


MVC4 controller:
C#
[ValidateInput(false)]
    public class EmployeeController : BaseController
    {
        private IEmployeeRepository EmployeeR;

        public override void Init()
        {
            EmployeeR = new EmployeeRepository(new EmployeePersistor(db.Session), new EmployeeFinder());
        }
...
[HttpPost]
        public ActionResult Edit(Employee employee)
        {
            try
            {
                if (ModelState.IsValid)
                {
                    employee.Phones = (IList<Phone>)Session["Phones"];
                    IList<string> errors = EmployeeR.Save(employee);
                    Session["Phones"] = null;
                    return RedirectToAction("Index");
                }
            }
            catch (Exception ex)
            {
            }

            return PartialView(employee);
        }
...
}


After I fill all the information of the Employee and add collection of phones, when I press save, the information haven't been saved in the database. After some debugging, I found that when my program reaches to "Session.SaveOrUpdate(entity);" the exception above appeared.
How to solve this issue?
Posted
Updated 30-Jul-14 2:57am
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900