代码之家  ›  专栏  ›  技术社区  ›  Rookian

NHibernate-如何处理ununiqueobjectexception?

  •  2
  • Rookian  · 技术社区  · 14 年前

    我得到这个错误:

    标识符值已存在 与会议有关:63,共 实体:核心.Domain.Model.员工

    在我的ASP.NETMVC控制器操作我这样做:

    public ActionResult Index(int? page)
    {
        int totalCount;
        Employee[] empData = _employeeRepository.GetPagedEmployees(page ?? 1, 5, out totalCount);
    
        EmployeeForm[] data = (EmployeeForm[]) Mapper<Employee[], EmployeeForm[]>(empData);
    
        PagedList<EmployeeForm> list = new PagedList<EmployeeForm>(data, page ?? 1, 5, totalCount);
    
    
        if (Request.IsAjaxRequest())
            return View("Grid", list);
    
        return View(list);
    }
    
    public ActionResult Edit(int id)
    {
        ViewData[Keys.Teams] = MvcExtensions.CreateSelectList(
            _teamRepository.GetAll().ToList(), 
            teamVal => teamVal.Id, 
            teamText => teamText.Name);
        return View(_employeeRepository.GetById(id) ?? new Employee());
    }
    
    [HttpPost]
    public ActionResult Edit(
        Employee employee, 
        [Optional, DefaultParameterValue(0)] int teamId)
    {
        try
        {
            var team = _teamRepository.GetById(teamId);
            if (team != null)
            {
                employee.AddTeam(team);
            }
    
            _employeeRepository.SaveOrUpdate(employee);
    
            return Request.IsAjaxRequest() ? Index(1) : RedirectToAction("Index");
        }
        catch
        {
            return View();
        }
    }
    

    映射文件:

    雇员

    public sealed class EmployeeMap : ClassMap<Employee>
    {
        public EmployeeMap()
        {
            Id(p => p.Id)
                .Column("EmployeeId")
                .GeneratedBy.Identity();
    
            Map(p => p.EMail);
            Map(p => p.LastName);
            Map(p => p.FirstName);
    
            HasManyToMany(p => p.GetTeams())
                .Access.CamelCaseField(Prefix.Underscore)
                .Table("TeamEmployee")
                .ParentKeyColumn("EmployeeId")
                .ChildKeyColumn("TeamId")
                .LazyLoad()
                .AsSet()
                .Cascade.SaveUpdate();
    
            HasMany(p => p.GetLoanedItems())
                .Access.CamelCaseField(Prefix.Underscore)
                .Cascade.SaveUpdate()
                .KeyColumn("EmployeeId");
        }
    }
    

    团队:

    public sealed class TeamMap : ClassMap<Team>
    {
        public TeamMap()
        {
            Id(p => p.Id)
                .Column("TeamId")
                .GeneratedBy.Identity();
    
            Map(p => p.Name);
    
            HasManyToMany(p => p.GetEmployees())
                .Access.CamelCaseField(Prefix.Underscore)
                .Table("TeamEmployee")
                .ParentKeyColumn("TeamId")
                .ChildKeyColumn("EmployeeId")
                .LazyLoad()
                .AsSet()
                .Inverse()
                .Cascade.SaveUpdate();
        }
    }
    

    我如何修复这个错误,这里有什么问题?

    2 回复  |  直到 14 年前
        1
  •  10
  •   Stefan Steinegger    14 年前

    您很可能正在将两个具有相同id的员工添加到会话中。

    找出为什么有两个员工拥有相同的id。

    var employee = employeeRepository.GetById(detachedEmployee.id);
    var team = _teamRepository.GetById(teamId);
    if (team != null)
    {
        employee.AddTeam(team);
    }
    _employeeRepository.SaveOrUpdate(employee);
    

    当您要在 detachedEmployee session.Merge 而不是 session.Update (或 session.SaveOrUpdate

    var employee = employeeRepository.Merge(detachedEmployee);
    // ...
    

    这个错误还有其他原因,但我不知道你在做什么,也不知道你的映射文件是什么样子。


    // put the employee into the session before any query.
    _employeeRepository.SaveOrUpdate(employee);
    
    var team = _teamRepository.GetById(teamId);
    if (team != null)
    {
        employee.AddTeam(team);
    }
    

    或使用合并:

    var team = _teamRepository.GetById(teamId);
    if (team != null)
    {
        employee.AddTeam(team);
    }
    // use merge, because there is already an instance of the
    // employee loaded in the session.
    // note the return value of merge: it returns the instance in the
    // session (but take the value from the given instance)
    employee = _employeeRepository.Merge(employee);
    

    说明:

    内存中只能有同一数据库“record”的一个实例。NH确保查询返回的实例与会话缓存中的实例相同。如果不是这样,那么对于同一个数据库字段,内存中会有多个值。这是不一致的。

    当一个实例已经在会话中(例如通过查询)并且试图将另一个具有相同id的实例(例如分离/序列化的实例)放入会话中(例如使用update)时,就会出现问题。

    解决方案一将实例放入会话 任何其他查询。NH将在所有后续查询中准确返回此实例!这真的很好,但是您需要在任何查询之前调用Update以使其正常工作。

    解决方案二使用合并。合并执行以下操作:

    • 如果实例已经在缓存中,它会将参数中的所有属性写入缓存中的属性,并返回缓存中的属性。
    • 如果实例不在缓存中,它将从数据库中检索它(这需要一个查询)。然后将参数添加到缓存中。它还返回缓存中与参数相同的实例。与简单的更新不同,它不会盲目地更新数据库中的值,而是首先检索它。这允许NH执行插入或更新,完全忽略更新(因为它是不变的)和一些更特殊的情况。


    编辑: 第二个雇员实例

    [HttpPost]
    public ActionResult Edit(
        Employee employee, // <<== serialized (detached) instance
        [Optional, DefaultParameterValue(0)] int teamId)
    {
        // ...
    
        // load team and containing Employees into the session
        var team = _teamRepository.GetById(teamId);
    
    
        // ...
    
        // store the detached employee. The employee may already be in the session,
        // loaded by the team query above. The detached employee is another instance
        // of an already loaded one. This is not allowed.
        _employeeRepository.SaveOrUpdate(employee);
    
        // ...
    }
    
        2
  •  0
  •   David Perlman    14 年前

    Team.Employee 加载方式与 employee 在你的代码里。试着设置你的 team.Employee 给这里的那个。