**此问题已经过编辑,以使其更简单,更集中**
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
答案 0 :(得分:2)
虽然这是可行的,但最好在数据库中使用它(使用标识或触发器)并将属性映射为插入时生成的属性。
答案 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)