问题描述
|
**此问题已过编辑,使其更简单,更集中**
Employee有一个EmployeeNumberValue属性,我希望该属性由数据库自动递增。对于业务域,这是分配给员工的唯一ID,用于在员工卡等上标识他们。但是,对于数据库,它是备用ID,而不是主键。
NHib具有记录的功能,称为“生成的属性”。
根据文档,\“生成的属性是具有其值由数据库生成的属性。通常,NHibernate应用程序需要刷新对象,这些对象包含数据库为其生成值的任何属性。但是,将属性标记为已生成,则可以让应用程序委托本质上,每当NHibernate对已定义生成属性的实体发出sql INSERT或UPDATE时,NHibernate都会立即发出选择以检索生成的值。\“
我遇到的问题是,尽管NHib进行了其他SELECT更新EmployeeNumberValue,但它没有将检索到的值分配给该属性。
谁能知道为什么会这样吗?
干杯,
贝里
失败的测试和输出(在内存数据库中通过sqlite进行了测试):
[Test]
public void Employee_OnInsert_EmployeeNumberValueIsIncremented() {
var emp1 = new Employee
{
FullName = _fullName,Department = _department,};
var emp2 = new Employee
{
FullName = _fullName,};
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,@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>
<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
解决方法
从设计的角度来看,在这种情况下,我不会依赖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;
}
}
, 尽管这是可行的,但最好在数据库中执行此操作(使用标识或触发器),并映射插入时生成的属性。
检查5.5。生成的属性
, 我有同样的情况,在生产中效果很好。
这是映射(由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)