我很难理解为什么当我从MyUser.Settings和SAVE MyUser中删除子设置对象时,我会收到如下SQL错误:
Cannot insert the value NULL into column 'MyUserId', table '###.Settings'; column does not allow nulls. UPDATE fails.
The statement has been terminated.
我期望发生的是从集合中删除项目,然后保存MyUser会导致NHibernate为给定的子项发出DELETE命令。但是,它所做的是UPDATE Settings对象的相关行,将MyUserId设置为NULL - 这是不允许的,因为我正在使用复合键。
我尝试了很多Inverse()和各种Cascade选项的组合,但似乎没有任何效果。我应该指出,当我保存MyUser时,添加到集合中的效果非常好。
我完全不知所措!
下面是伪代码,用于尝试解释我的实体和映射。
public class SettingType
{
public virtual int SettingTypeId { get; set; }
public virtual string Name { get; set; }
public virtual bool Active { get; set; }
}
public class Setting
{
public virtual MyUser MyUser { get; set; }
public virtual SettingType SettingType { get; set; }
public virtual DateTime Created { get; set; }
}
public class MyUser
{
public virtual int MyUserId { get; set; }
public virtual IList<Setting> Settings { get; set; }
public virtual string Email { get; set; }
public void AddSetting(SettingType settingType, DateTime now)
{
var existing = _settings.SingleOrDefault(s => s.SettingType.SettingTypeId == settingType.SettingTypeId);
if (existing != null)
{
existing.Updated = now;
}
else
{
var setting = new Setting
{
MyUser = this,
SettingType = settingType,
Created = now,
};
_settings.Add(setting);
}
}
public void RemoveSetting(SettingType settingType)
{
var existingPref = _settings.SingleOrDefault(s => s.SettingType.SettingTypeId == settingType.SettingTypeId);
if (existingPref != null)
{
_settings.Remove(existingPref);
}
}
private readonly IList<Setting> _settings = new List<Setting>();
}
我的映射:
public class SettingTypeMap : IAutoMappingOverride<SettingType>
{
public void Override(AutoMapping<SettingType> mapping)
{
mapping.Table("SettingTypes");
mapping.Id(m => m.SettingTypeId).GeneratedBy.Identity();
mapping.Map(m => m.Name).Not.Nullable().Length(100);
mapping.Map(m => m.Active).Not.Nullable().Default("0");
}
}
public class SettingMap : IAutoMappingOverride<Setting>
{
public void Override(AutoMapping<Setting> mapping)
{
mapping.Table("Settings");
mapping.CompositeId()
.KeyReference(m => m.MyUser)
.KeyReference(m => m.SettingType);
mapping.Map(m => m.Created).Not.Nullable().Default("CURRENT_TIMESTAMP");
mapping.Map(m => m.Updated).Nullable();
}
}
public class MyUserMappingOverride : IAutoMappingOverride<MyUser>
{
public void Override(AutoMapping<MyUser> mapping)
{
mapping.Table("MyUsers");
mapping.Id(m => m.MyUserId).GeneratedBy.Identity();
mapping.Map(m => m.Email).Not.Nullable().Length(200);
mapping.HasMany(m => m.Settings).KeyColumn("MyUserId").Cascade.DeleteOrphan()
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
}
}
全部使用:
FluentNHibernate v1.3.0.733
NHibernate v3.3.1.4000
更新:在提出一些建议之后,我尝试更改MyUser实体的映射。
首先:
mapping.HasMany(m => m.Settings)
.KeyColumn("MyUserId")
.Inverse()
.Cascade.DeleteOrphan()
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
这给出了错误:词典中没有给定键
因此尝试添加第二个关键列:
mapping.HasMany(m => m.Settings)
.KeyColumn("MyUserId")
.KeyColumn("SettingTypeId")
.Inverse()
.Cascade.DeleteOrphan()
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
但是,当从给定MyUserId的DB加载Settings集合时,这会导致奇怪的行为。查看nh探查器,我看到第二个SELECT ... FROM Settings,但设置SettingTypeId与MyUserId的值相同。
仍然完全不知所措。花了我太多时间,所以要恢复为设置实体添加主键ID字段。也许你只是不能做我正在尝试使用NHibernate。在纯SQL中,这很简单。
答案 0 :(得分:2)
您应该使用反向映射
mapping.HasMany(m => m.Settings)
.KeyColumn("MyUserId")
.Inverse()
.Cascade.DeleteOrphan()
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
这将允许NHibernate询问设置本身是否被删除。否则,NHibernate首先尝试删除该关系,并尝试删除该实体。
请参阅:6.4. One-To-Many Associations
非常重要注意:如果列的一个 关联声明为NOT NULL,NHibernate可能会导致约束 创建或更新关联时的违规。为了防止这种情况 问题,你必须使用与许多有价值的双向关联 结束(集合或包)标记为inverse =“true”。参见讨论 本章后面的双向关联。