非法尝试将一个集合与两个打开的会话关联

本文关键字:两个 关联 会话 一个 集合 非法 | 更新日期: 2023-09-27 18:15:54

我有这个异常"非法尝试将一个集合与两个打开的会话关联",每次我保存包含子集合的实体时都会引发这个异常。我谷歌一下。我发现我在调用save时打开了两个或更多的会话,但我确信我只使用了一个会话。我哪里做错了?我该如何解决这个问题?注意:我正在使用MVC4,和流利的NHibernate。

实体:

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; }
}

映射:

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();
    }
}

存储库:

public abstract class RepositoryBase<TEntity, TIdentity>
    : IRepository<TEntity, TIdentity>
    where TEntity : EntityBase<TIdentity>
    where TIdentity : IComparable
{
    private readonly IPersistor<TEntity, TIdentity> persistor; //contains the session to operate with the database
    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();
    }
}
public class EmployeeRepository : RepositoryBase<Employee, int>, IEmployeeRepository
{
    public EmployeeRepository(
        IPersistor<Employee, int> persistor,
        IEmployeeFinder entityFinder)
        : base(persistor, entityFinder) { }
    public IEmployeeFinder Find
    {
        get { return (IEmployeeFinder)Finder; }
    }
}
public class PhoneRepository : RepositoryBase<Phone, int>, IPhoneRepository
{
    public PhoneRepository(
        IPersistor<Phone, int> persistor,
        IPhoneFinder entityFinder)
        : base(persistor, entityFinder) { }
    public IPhoneFinder Find
    {
        get { return (IPhoneFinder)Finder; }
    }
}

在填写完员工的所有信息并添加电话集合后,当我按下保存键时,这些信息还没有保存到数据库中。经过一些调试,我发现当我的程序到达"Session.SaveOrUpdate(entity);"时,出现了上面的异常。如何解决这个问题?

非法尝试将一个集合与两个打开的会话关联

另外,为了完整起见,通常有两种类型的问题:

  1. 通常与使用自己的ISession创建的不正确管理的DAO对象相关:

    在上面定义的示例中,可以有两个对象与存储库一起工作:(a) persistor和(b) finder

    它们各有一个ISession实例。如果finder成功并找到一些东西(例如Phones),则请求它们的实体Employee尝试保存,但是Phones位于与Employee不同的会话中。

  2. 通常与ASP相关。. NET MVC及其Redirect()动作结果:

    最有可能的手机由不同的会话加载。而不是处理Employee的"current"。

    所以最可疑的是调用Redirect()。如果是,我们在一个控制器生命周期中加载对象-将其放入Temp字典-调用重定向另一个控制器 -现在有一个新会话以及与旧的关闭会话关联的旧对象。

解决方案:确保所有DAO处理都是一个ISession作用域的一部分。不要在会话之间传输任何数据(也不要在控制器重定向之间)

我在使用静态变量的每个方法中使用锁语句来解决这个问题,因为问题的根源与同步有关。

下面是一个简单的例子来说明我的解决方案:

private static int sharedVariable;
private static object _syncronizationObject = new Object();
public void MethodThatUsesStaticVariable(int newValue)
{
    // This lock prevents concurrency problems, and this is what solved the issue for me.
    lock(_syncronizationObject)
    {
        sharedVariable = newValue;
    }
}