使用复合键,Fluent NHibernate映射/父/子删除失败

时间:2013-06-06 10:47:16

标签: c# nhibernate fluent-nhibernate

我很难理解为什么当我从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中,这很简单。

1 个答案:

答案 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”。参见讨论   本章后面的双向关联。