简体   繁体   English

流利的NHibernate映射/使用组合键删除父/子项失败

[英]Fluent NHibernate Mapping / Parent/Child delete fail using composite key

I'm struggling to understand why when I remove a child Settings object from MyUser.Settings and SAVE MyUser I get SQL errors like below: 我正在努力理解为什么当我从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.

What I would expect to happen is that removing the item from the collection, then saving MyUser causes NHibernate to issue a DELETE command for the given child. 我希望发生的是从集合中删除项目,然后保存MyUser导致NHibernate对给定的孩子发出DELETE命令。 However, what it does is UPDATE the relevant row for the Settings object, setting MyUserId to NULL - which isn't allowed as I'm using a Composite Key. 但是,它所做的是更新Settings对象的相关行,将MyUserId设置为NULL-不允许,因为我使用的是Composite Key。

I've tried so many combinations of Inverse() and the various Cascade options but nothing seems to work. 我尝试了Inverse()和各种Cascade选项的许多组合,但似乎没有任何效果。 I should point out that Adding to the collection works perfectly when I save MyUser. 我应该指出,保存MyUser时,将其添加到集合中是完美的。

I'm totally baffled! 我完全感到困惑!

Below is pseudo code to try and explain my entities and mappings. 下面是尝试解释我的实体和映射的伪代码。

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>();
}

And my mappings: 和我的映射:

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);
    }
}

All using: 全部使用:

FluentNHibernate v1.3.0.733 FluentNHibernate v1.3.0.733

NHibernate v3.3.1.4000 NHibernate v3.3.1.4000

UPDATE: After a few suggestions I've tried to change the mapping for MyUser entity. 更新:在提出了一些建议之后,我尝试更改MyUser实体的映射。

First to this: 首先:

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

This gives the error: Given key was not present in the dictionary 这给出了错误:字典中不存在给定键

So tried to add second key column: 因此尝试添加第二个关键列:

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

But this then causes odd behaviour when loading the Settings collection from the DB for a given MyUserId. 但这会在从数据库加载给定MyUserId的Settings集合时导致奇怪的行为。 Looking at the nh profiler I see a second SELECT ... FROM Settings but setting the SettingTypeId same as value for MyUserId. 看着nh分析器,我看到第二个SELECT ... FROM Settings,但是将SettingTypeId设置为MyUserId的值。

Still totally baffled. 仍然完全困惑。 Has cost me too much time so going to revert to adding a primary key id field to the Settings entity. 花费了我太多时间,因此恢复了将主键id字段添加到Settings实体的过程。 Maybe you just can't do what I'm trying using NHibernate. 也许您无法使用NHibernate执行我正在尝试的操作。 In pure SQL this is simple. 在纯SQL中,这很简单。

You should use the Inverse mapping 您应该使用逆映射

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

This will allow NHibernate to ask the setting itself to be deleted. 这将允许NHibernate要求删除设置本身。 Otherwise, NHibernate firstly tries to delete the relation, and would try to delete the entity. 否则,NHibernate首先尝试删除该关系,然后尝试删除该实体。

See: 6.4. 请参阅: 6.4。 One-To-Many Associations 一对多协会

Very Important Note: If the column of a association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. 非常重要的说明:如果关联的列声明为NOT NULL,则NHibernate在创建或更新关联时可能会导致约束冲突。 To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse="true". 为避免此问题,您必须使用双向关联,并将多个有价值的末端(集合或袋子)标记为inverse =“ true”。 See the discussion of bidirectional associations later in this chapter. 请参阅本章后面的双向关联讨论。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM