Nhibernate:一对多映射问题 - 无法级联删除而无需逆向。设置 NULL 错误

发布于 2024-08-30 16:13:10 字数 3030 浏览 2 评论 0原文

我目前的情况是,每篇文章只有 1 个结果。每篇文章可能有也可能没有结果。

理论上,这是一对一的映射,但是由于NHibernate并不真正支持一对一,所以我使用了一对多来替代。我在子表上的主键实际上是ArticleID (FK)。

所以我有以下设置:

public class Article
{
    public virtual Int32 ID { get;set;}
    private ICollection<ArticleOutcome> _Outcomes {get;set;}

    public virtual ArticleOutcome Outcome
    {
        get {
            if( this._Outcomes !=null && this._Outcomes.Count > 0 )
                return this._Outcomes.First();
            return null;
        }
        set {
            if( value == null ) {
                if( this._Outcomes !=null && this._Outcomes.Count > 0 )
                    this._Outcomes.Clear();
            }
            else {
                if( this._Outcomes == null )
                    this._Outcomes = new HashSet<ArticleOutcome>();
                else if ( this._Outcomes.Count >= 1 )
                    this._Outcomes.Clear();
                this._Outcomes.Add( value );
            }
        }
    }
}

public class ArticleOutcome
{
    public virtual Int32 ID { get;set; }
    public virtual Article ParentArticle { get;set;}
}

映射

public class ArticleMap : ClassMap<Article> 
{
    public ArticleMap() {
        this.Id( x=> x.ID ).GeneratedBy.Identity();

        this.HasMany<ArticleOutcome>( Reveal.Property<Article>("_Outcomes") )
            .AsSet().KeyColumn("ArticleID")
            .Cascade.AllDeleteOrphan() //Cascade.All() doesn't work too.
            .LazyLoad()
            .Fetch.Select();
    }
}

public class ArticleOutcomeMap : ClassMap<ArticleOutcome>
{
    public ArticleOutcomeMap(){
       this.Id( x=> x.ID, "ArticleID").GeneratedBy.Foreign("ParentArticle");

       this.HasOne( x=> x.ParentArticle ).Constrained ();

       //This do not work also.
       //this.References( x=> x.ParentArticle, "ArticleID" ).Not.Nullable(); 
    }
}

现在我的问题是这样的:

当我插入/更新结果时它起作用。 例如,

var article = new Article();
article.Outcome = new ArticleOutcome { xxx = "something" };
session.Save( article );

但是,当我尝试通过文章本身删除时,我遇到了 SQL 错误。

var article = session.Get( 123 );
session.Delete( article ); //throws SQL error.

该错误类似于无法将 NULL 插入 ArticleOutcome 表中的 ArticleID

如果我将 Inverse() 放置到文章的 HasMany() 映射中,则删除有效,但插入将会失败。

有人有解决方案吗?或者我真的必须向 ArticleOutcome 表添加代理键吗?


解决方案

如果有人感兴趣的话,这是 Fluent 的映射。

public class ArticleMap : ClassMap<Article> 
{
    public ArticleMap() {
        this.Id( x=> x.ID ).GeneratedBy.Identity();

        this.HasOne( x=> x.Outcome ).Cascade.All();
    }
}


public class Article 
{
    //... other properties

    public virtual ArticleOutcome Outcome { get;set;}
}

I have the current scenario whereby an Article has only 1 Outcome each. Each Article may or may not have an Outcome.

In theory, this is a one-to-one mapping, but since NHibernate does not really support one-to-one, I used a One-To-Many to substitute. My Primary Key on the child table is actually the ArticleID (FK).

So I have the following setup:

Classes

public class Article
{
    public virtual Int32 ID { get;set;}
    private ICollection<ArticleOutcome> _Outcomes {get;set;}

    public virtual ArticleOutcome Outcome
    {
        get {
            if( this._Outcomes !=null && this._Outcomes.Count > 0 )
                return this._Outcomes.First();
            return null;
        }
        set {
            if( value == null ) {
                if( this._Outcomes !=null && this._Outcomes.Count > 0 )
                    this._Outcomes.Clear();
            }
            else {
                if( this._Outcomes == null )
                    this._Outcomes = new HashSet<ArticleOutcome>();
                else if ( this._Outcomes.Count >= 1 )
                    this._Outcomes.Clear();
                this._Outcomes.Add( value );
            }
        }
    }
}

public class ArticleOutcome
{
    public virtual Int32 ID { get;set; }
    public virtual Article ParentArticle { get;set;}
}

Mappings

public class ArticleMap : ClassMap<Article> 
{
    public ArticleMap() {
        this.Id( x=> x.ID ).GeneratedBy.Identity();

        this.HasMany<ArticleOutcome>( Reveal.Property<Article>("_Outcomes") )
            .AsSet().KeyColumn("ArticleID")
            .Cascade.AllDeleteOrphan() //Cascade.All() doesn't work too.
            .LazyLoad()
            .Fetch.Select();
    }
}

public class ArticleOutcomeMap : ClassMap<ArticleOutcome>
{
    public ArticleOutcomeMap(){
       this.Id( x=> x.ID, "ArticleID").GeneratedBy.Foreign("ParentArticle");

       this.HasOne( x=> x.ParentArticle ).Constrained ();

       //This do not work also.
       //this.References( x=> x.ParentArticle, "ArticleID" ).Not.Nullable(); 
    }
}

Now my problem is this:

It works when I do an insert/update of the Outcome.
e.g.

var article = new Article();
article.Outcome = new ArticleOutcome { xxx = "something" };
session.Save( article );

However, I encounter SQL errors when attempting to delete via the Article itself.

var article = session.Get( 123 );
session.Delete( article ); //throws SQL error.

The error is something to the like of Cannot insert NULL into ArticleID in ArticleOutcome table.

The deletion works if I place Inverse() to the Article's HasMany() mapping, but insertion will fail.

Does anyone have a solution for this? Or do I really have to add a surrogate key to the ArticleOutcome table?


Solution

Here's the mapping for Fluent if anyone is interested.

public class ArticleMap : ClassMap<Article> 
{
    public ArticleMap() {
        this.Id( x=> x.ID ).GeneratedBy.Identity();

        this.HasOne( x=> x.Outcome ).Cascade.All();
    }
}


public class Article 
{
    //... other properties

    public virtual ArticleOutcome Outcome { get;set;}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ζ澈沫 2024-09-06 16:13:10

NHibernate 确实支持一对一。

我有一个案例和你的情况非常相似。以下是相关部分:

class Articule
{
    public virtual ArticleOutcome Outcome { get; set; }
}

class ArticuleOutcome
{
    public virtual Article ParentArticle { get; set; }
}

映射(抱歉,我不使用 Fluent,但翻译起来应该不难):

<class name="Article">
  <id name="ID">
    <generator class="identity"/>
  </id>
  <one-to-one name="Outcome" cascade="all"/>
</class/>
<class name="ArticleOutcome">
  <id name="ID" column="ArticleId">
    <generator class="foreign">
      <param name="property">ParentArticle</param>
    </generator>
  </id>
  <one-to-one name="ParentArticle" constrained="true"/>
</class/>

用法:

var article = new Article();
article.Outcome = new ArticleOutcome
                  {
                      xxx = "something",
                      ParentArticle = article
                  };
session.Save(article);

删除应该像您现在使用的那样工作。

NHibernate does support one-to-one.

I have a case very similar to yours. Here are the relevant parts:

class Articule
{
    public virtual ArticleOutcome Outcome { get; set; }
}

class ArticuleOutcome
{
    public virtual Article ParentArticle { get; set; }
}

Mapping (sorry, I don't use Fluent, but it shouldn't be hard to translate):

<class name="Article">
  <id name="ID">
    <generator class="identity"/>
  </id>
  <one-to-one name="Outcome" cascade="all"/>
</class/>
<class name="ArticleOutcome">
  <id name="ID" column="ArticleId">
    <generator class="foreign">
      <param name="property">ParentArticle</param>
    </generator>
  </id>
  <one-to-one name="ParentArticle" constrained="true"/>
</class/>

Usage:

var article = new Article();
article.Outcome = new ArticleOutcome
                  {
                      xxx = "something",
                      ParentArticle = article
                  };
session.Save(article);

Delete should work as you are using it now.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文