1

我很难理解为什么当我从 MyUser.Settings 和 SAVE MyUser 中删除子 Settings 对象时,我会收到如下 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 命令。但是,它所做的是更新 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 加载设置集合时,这会导致奇怪的行为。查看 nh 探查器,我看到第二个 SELECT ... FROM Settings,但将 SettingTypeId 设置为与 MyUserId 的值相同。

还是完全一头雾水。花了我太多时间,所以要恢复到向设置实体添加主键 id 字段。也许你不能做我正在尝试使用 NHibernate 的事情。在纯 SQL 中,这很简单。

4

1 回答 1

2

你应该使用逆映射

mapping.HasMany(m => m.Settings)
  .KeyColumn("MyUserId")
  .Inverse()
  .Cascade.DeleteOrphan()
  .Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);

这将允许 NHibernate 要求删除设置本身。否则,NHibernate 首先会尝试删除关系,然后会尝试删除实体。

见:6.4。一对多关联

非常重要的注意事项:如果关联的列被声明为 NOT NULL,NHibernate 在创建或更新关联时可能会导致违反约束。为防止此问题,您必须使用与标记为 inverse="true" 的多值端(集合或包)的双向关联。请参阅本章后面对双向关联的讨论。

于 2013-06-06T11:12:49.337 回答