nhibernate alternate id使用生成的属性

时间:2011-05-31 17:05:48

标签: nhibernate identity sequences

**此问题已经过编辑,以使其更简单,更集中**

Employee有一个EmployeeNumberValue属性,我想通过db自动增加它。对于业务域,这是分配给员工的唯一ID,用于在员工卡等上识别它们。但是,对于数据库,它是备用ID而不是主键。

NHib有一种名为Generated Properties的记录能力。 根据文档,“生成的属性是由数据库生成其值的属性。通常,NHibernate应用程序需要刷新包含数据库生成值的任何属性的对象。但是,将属性标记为已生成,可让应用程序委派此属性对NHibernate负责。基本上,每当NHibernate为已定义生成属性的实体发出SQL INSERT或UPDATE时,它会立即发出一个select来检索生成的值。“

我遇到的问题是,当NHib正在使用额外的SELECT来更新EmployeeNumberValue时,它不会将检索到的值分配给属性。

任何人都可以看到为什么会出现这种情况?

干杯,
Berryl

失败的测试和输出(在内存db中使用SQLite测试):

    [Test]
    public void Employee_OnInsert_EmployeeNumberValueIsIncremented() {

        var emp1 = new Employee
        {
            FullName = _fullName,
            Department = _department,
        };
        var emp2 = new Employee
        {
            FullName = _fullName,
            Department = _department,
        };

        var session = _SessionFactory.GetCurrentSession(); 

        using (var tx = session.BeginTransaction())
        {
            session.Save(_department);
            session.Save(emp1);
            session.Save(emp2);
            tx.Commit();
        }
        Assert.That(emp1.EmployeeNumberValue, Is.EqualTo(1));
        Assert.That(emp2.EmployeeNumberValue, Is.EqualTo(2));
    }

NHibernate: INSERT INTO Employees (FirstName, LastName, DepartmentId, EmployeeId) 
        VALUES (@p0, @p1, @p2, @p3);@p0 = 'Berryl' [Type: String (0)], @p1 = 'Hesh' [Type: String (0)], @p2 = 32768 [Type: Int32 (0)], @p3 = 65536 [Type: Int32 (0)]
NHibernate: SELECT employee_.EmployeeNumberValue as Employee2_1_ FROM Employees employee_ WHERE employee_.EmployeeId=@p0;@p0 = 65536 [Type: Int32 (0)]
NHibernate: INSERT INTO Employees (FirstName, LastName, DepartmentId, EmployeeId) 
        VALUES (@p0, @p1, @p2, @p3);@p0 = 'Berryl' [Type: String (0)], @p1 = 'Hesh' [Type: String (0)], @p2 = 32768 [Type: Int32 (0)], @p3 = 65537 [Type: Int32 (0)]
NHibernate: SELECT employee_.EmployeeNumberValue as Employee2_1_ FROM Employees employee_ WHERE employee_.EmployeeId=@p0;@p0 = 65537 [Type: Int32 (0)]
Test failed: 
   Expected: 1
   But was:  0

对象模型

public class Employee : Entity, IResource
{
    public virtual long EmployeeNumberValue { get; set; }

    ...
}

映射:

  <class name="Employee" table="Employees">

<id name="Id" unsaved-value="0">
  <column name="EmployeeId" />
  <generator class="hilo" />
</id>

<property name="EmployeeNumberValue" generated="insert" insert="false" update="false" >
  <column name="EmployeeNumberValue" sql-type="int IDENTITY(1,1)" index="IDX_EmployeeNumber"  />      
</property>

...

create table Employees (
    EmployeeId INTEGER not null,
   EmployeeNumberValue int IDENTITY(1,1),
   FirstName TEXT not null,
   LastName TEXT not null,
   DepartmentId INTEGER,
   primary key (EmployeeId)
)

我怀疑我标记列的方式,因为IDENTITY也是可疑的。我尝试使用如下所示的database-object,但这样做会出现使用错误

  <database-object>
    <create>
      ALTER TABLE Employee DROP COLUMN EmployeeNumberValue
      ALTER TABLE Employee ADD EmployeeNumberValue INT IDENTITY
    </create>
    <drop>
      ALTER TABLE Employee DROP COLUMN EmployeeNumberValue
    </drop>
  </database-object>

SQLiteException : SQLite error  "DROP": syntax error

3 个答案:

答案 0 :(得分:2)

虽然这是可行的,但最好在数据库中使用它(使用标识或触发器)并将属性映射为插入时生成的属性。

检查5.5. Generated Properties

答案 1 :(得分:1)

从设计角度来看,在这种情况下我不会依赖NHibernate。我的意思是,在您的域模型中,您希望员工获得新的员工卡号。

在这种情况下,如果有卡号,我只允许员工实例化。

public class EmployeeCardNumber
{
    private string id = String.Empty;
    internal EmployeeCardNumber(string id)
    {
        this.id = id;
    }
}


public class Employee
{
    private EmployeeCardNumber employeeCardNumber;

    public EmployeeCardNumber CardNumber { ... }

    public Employee(EmployeeCardNumber employeeCardNumber)
    {
        this.employeeCardNumber = employeeCardNumber;
    }
}

所以现在你必须考虑如何生成一个独特的EmployeeCardNumber。

public class EmployeeCardNumberFactory
{
    public EmployeeCardNumber CreateNew()
    {
        // in this example the card number will be a guid.
        // but you could also implement a "EmployeeCardNumberGenerator" class which will do crazy database stuff
        return new EmployeeCardNumber(Guid.NewGuid().ToString());
    }
}

然后你会做:

EmployeeCardNumber cardNumber = employeeCardNumberFactory.CreateNew();
Employee employee = new Employee(cardNumber, name, etc...);

<强>增加: 要通过数据库生成“EmployeeCardNumber”,您可以将“EmployeeCardNumber”映射到一个额外的表“EmployeeCardNumber”,它将作为您的身份生成器,如:

<class name="EmployeeCardNumber" table="EmployeeCardNumber">
    <id name="id" access="field" unsaved-value="0">
      <column name="EmployeeCardNumberId" />
      <generator class="identity" />
    </id>
</class>

然后在工厂你可以做到:

public class EmployeeCardNumberFactory
{
    private IEmployeeCardNumberRepository repository = new EmployeeCardNumberRepository(); // inject...
    public EmployeeCardNumber CreateNew()
    {     
        EmployeeCardNumber cardNumber = new EmployeeCardNumber();
        repository.Save(cardNumber); // gets you a fresh id
        return cardNumber;
    }
}

答案 2 :(得分:0)

我有相同的情景,它在制作中效果很好。

这是映射(由Fluent NHibernate生成):

<property generated="insert" name="Number" update="false" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
  <column name="Number" not-null="true" />
</property>

在数据库中,此列如下所示:

ALTER TABLE [DeviceLink] ADD [Number] INT not null IDENTITY (1, 1)
相关问题