1

MyUser.Settings から子設定オブジェクトを削除して 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 コマンドを発行することが予想されます。ただし、設定オブジェクトの関連行を更新し、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);

これにより、エラーが発生します:指定されたキーが辞書に存在しませんでした

したがって、2番目のキー列を追加しようとしました:

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

しかし、これにより、特定の MyUserId の DB から設定コレクションをロードするときに、奇妙な動作が発生します。nh プロファイラーを見ると、2 番目の 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 に答える