没有主键的NHibernate SaveOrUpdate

时间:2014-09-23 13:07:46

标签: nhibernate orm entity composite-key

情况

我有一个通过NHibernate(3.3.3-SP1)映射的数据库表。该应用程序在.NET4.0上运行,映射通过FluentNHibernate(1.4.0)完成。

CREATE TABLE Movies
(id INT PRIMARY KEY,
 yearPublished DATETIME NOT NULL,
 name NVARCHAR(500) NOT NULL,
 description NTEXT NOT NULL)

数据将是这样的:

id | yearPublished | name                   | description
---+---------------+------------------------+--------------------------------------------
1  | 1968          | 2001: A Space Oddyssey | An epic drama of adventure and exploration 

问题

我正在创建此表的新实体,并希望避免为同一个真实世界的事物添加多个实体。我知道有Session.SaveOrUpdate并且还有一种方法可以使它与复合和自然ID一起工作,但这不是我想要的,因为我的实体实际上有一个主键,我真的只需要复合键确保数据库中没有重复项。

var movie = new Movies
{
    yearPublished = 1968,
    name = "2001: A Space Oddyssey",
    description = "An awesome journey to Jupiter"
};

// Behavior right now:

// Adds a new movie besides the fact that
// the movie is already in the database
// but now has two entries
session.SaveOrUpdate(movie);

Assert.IsTrue(movie.id == 2 && movie.description == "An awesome journey to Jupiter");

// What I really want is to be able to define what
// makes an object unique other than the primary key;
// in this scenario it should look for a combination
// of "yearPublished" and "name"
session.MyAwesomeSaveOrUpdate(movie);

Assert.IsTrue(movie.id == 1 && movie.description == "An epic drama of adventure and exploration");

这个功能是否适用于NHibernate(例如通过自定义映射),或者我是否从数据库中取出候选人并手动完成?

谢谢!

1 个答案:

答案 0 :(得分:0)

我通过在数据库中的自然键字段上添加一个唯一约束并使用异常转换器将SQL Server异常转换为我的应用程序可以处理的异常来解决此问题。

public class SqlServerExceptionConverter : ISQLExceptionConverter
{
    public Exception Convert(AdoExceptionContextInfo adoExceptionContextInfo)
    {
        var sqlException = adoExceptionContextInfo.SqlException as SqlException;
        if (sqlException != null)
        {
            // 2601 is unique key, 2627 is unique index; same thing: 
            // http://blog.sqlauthority.com/2007/04/26/sql-server-difference-between-unique-index-vs-unique-constraint/
            if (sqlException.Number == 2601 || sqlException.Number == 2627)
            {
                // my custom exception
                return new UniqueKeyException(sqlException.Message, sqlException);
            }
        }
        return adoExceptionContextInfo.SqlException;
    }
}

我能想到的另一种方法是在插入之前向数据库查询匹配的记录,但这不是万无一失的,因为可以在select和insert之间插入一条记录。