nhibernate替换id使用生成的属性。

时间:2022-08-24 15:40:23

** This question has been edited to make it simpler and more focused **

这个问题经过编辑,使它更简单、更集中。

Employee has an EmployeeNumberValue property which I would like to have auto-incremented by the db. To the business domain, this is a unique id assigned to employees and used to identify them on employee cards, etc. To the database however, it is an alternate id and not the primary key.

Employee有一个EmployeeNumberValue属性,我希望它能自动递增。对于业务领域,这是分配给员工的惟一id,用于在员工卡片上标识它们。

NHib has a documented ability called Generated Properties. Per the docs, "generated properties are properties which have their values generated by the database. Typically, NHibernate applications needed to Refresh objects which contain any properties for which the database was generating values. Marking properties as generated, however, lets the application delegate this responsibility to NHibernate. Essentially, whenever NHibernate issues an SQL INSERT or UPDATE for an entity which has defined generated properties, it immediately issues a select afterwards to retrieve the generated values."

NHib具有被称为生成属性的文档化能力。在文档中,“生成的属性是具有由数据库生成的值的属性。通常,NHibernate应用程序需要刷新对象,这些对象包含数据库生成值的任何属性。但是,在生成的标记属性中,应用程序将此责任委托给NHibernate。实际上,每当NHibernate为一个定义生成属性的实体发出SQL INSERT或UPDATE语句时,它都会立即发出select,以检索生成的值。

The problem I am having is that while NHib is making the additional SELECT to update the EmployeeNumberValue, it is not assigning the retrieved value to the property.

我的问题是,当NHib正在进行额外的SELECT来更新EmployeeNumberValue时,它并没有将检索到的值分配给属性。

Can anyone see why this is happening what the fix is?

有人知道为什么会发生这样的问题吗?

Cheers,
Berryl

欢呼,Berryl

FAILING TEST AND OUTPUT (tested w/ SQLite in memory db):

失败测试和输出(在内存db中测试w/ 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

OBJECT MODEL

对象模型

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

    ...
}

MAPPING:

映射:

  <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)
)

I suspect the way I am marking the column as IDENTITY is also suspect. I tried using database-object as below, but got a usage error in doing so

我怀疑我作为身份标识的方式也是可疑的。我尝试使用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 个解决方案

#1


1  

From a design perpective I wouldn't rely on NHibernate in this case. What I mean is, that in your domain model, you want an employee to get a new employee card number.

从设计的角度来看,在这种情况下我不会依赖NHibernate。我的意思是,在你的领域模型中,你想要一个员工得到一个新的员工卡号。

In this case I would only allow an employee to be instantiated if there is a card number.

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

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

So now you have to think about how to generate a unique 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());
    }
}

Then you would later do:

然后你会:

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

Addition: To generate a "EmployeeCardNumber" via database, you could just map "EmployeeCardNumber" to an extra table "EmployeeCardNumber" that will serve as your identity generator like:

另外:通过数据库生成“EmployeeCardNumber”,您可以将“EmployeeCardNumber”映射到一个额外的表“EmployeeCardNumber”,它将作为您的身份生成器:

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

Then in the factory you could do:

然后在工厂你可以做:

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


2  

While that's doable, it's better to do it in the DB (using identity or a trigger) and map the property as generated on insert.

虽然这是可行的,但最好在DB(使用标识或触发器)中进行,并将属性映射到insert中生成的属性。

Check 5.5. Generated Properties

5.5检查。生成的属性

#3


0  

I had same scenario and it works very well in production.

我有同样的场景,而且在生产中也很好用。

Here is mapping (generated by Fluent NHibernate):

这里是映射(由流畅的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>

In database this column looks like this:

在数据库中,这个列是这样的:

ALTER TABLE [DeviceLink] ADD [Number] INT not null IDENTITY (1, 1)

#1


1  

From a design perpective I wouldn't rely on NHibernate in this case. What I mean is, that in your domain model, you want an employee to get a new employee card number.

从设计的角度来看,在这种情况下我不会依赖NHibernate。我的意思是,在你的领域模型中,你想要一个员工得到一个新的员工卡号。

In this case I would only allow an employee to be instantiated if there is a card number.

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

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

So now you have to think about how to generate a unique 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());
    }
}

Then you would later do:

然后你会:

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

Addition: To generate a "EmployeeCardNumber" via database, you could just map "EmployeeCardNumber" to an extra table "EmployeeCardNumber" that will serve as your identity generator like:

另外:通过数据库生成“EmployeeCardNumber”,您可以将“EmployeeCardNumber”映射到一个额外的表“EmployeeCardNumber”,它将作为您的身份生成器:

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

Then in the factory you could do:

然后在工厂你可以做:

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


2  

While that's doable, it's better to do it in the DB (using identity or a trigger) and map the property as generated on insert.

虽然这是可行的,但最好在DB(使用标识或触发器)中进行,并将属性映射到insert中生成的属性。

Check 5.5. Generated Properties

5.5检查。生成的属性

#3


0  

I had same scenario and it works very well in production.

我有同样的场景,而且在生产中也很好用。

Here is mapping (generated by Fluent NHibernate):

这里是映射(由流畅的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>

In database this column looks like this:

在数据库中,这个列是这样的:

ALTER TABLE [DeviceLink] ADD [Number] INT not null IDENTITY (1, 1)