【问题标题】:fluent nhibernate mapping issue: many-to-one...could not insert: [Entity]流畅的休眠映射问题:多对一...无法插入:[实体]
【发布时间】:2012-03-30 18:50:19
【问题描述】:

我在 Foo 和 Bar 之间有一对多的关系,我无法执行插入(到目前为止我只尝试过 select 和 insert...select 似乎工作得很好,每次插入都失败)。

表定义:

CREATE TABLE [Bar](
[Id] [int] IDENTITY(1,1) NOT NULL,
[DateField] [datetime] NULL,
[StringField] [varchar](8000) NULL
CONSTRAINT [PK_Bar] PRIMARY KEY CLUSTERED([Id] ASC)) ON PRIMARY

CREATE TABLE [Foo](
[Id] [int] IDENTITY(1,1) NOT NULL,
[BarId] [int] NOT NULL,
[DateField] [date] NOT NULL,
[StringField] [varchar](100) NOT NULL,
[GorpId] [int] NOT NULL,
[BoolField] [bit] NOT NULL
CONSTRAINT [PK_Foo] PRIMARY KEY CLUSTERED([Id] ASC))

ALTER TABLE [dbo].[Foo]  WITH CHECK ADD  CONSTRAINT [FK_Foo_Bar] FOREIGN KEY([BarId])
REFERENCES [dbo].[Bar] ([Id])
GO

类定义:

public class Foo
{
    public virtual int Id { get; set; }
    public virtual Bar Bar{ get; set; }
    public virtual DateTime DateField{ get; set; }
    public virtual string StringField{ get; set; }
    public virtual Gorp Gorp{ get; set; }
    public virtual bool BoolField{ get; set; }
}

public class Bar
{
    public virtual int Id { get; set; }
    public virtual DateTime DateField{ get; set; }
    public virtual string StringField{ get; set; }

    public virtual ICollection<Foo> Foos{ get; set; }

    public Bar()
    {
        Foos= new List<Foo>();
    }

    public virtual void AddFoo(Foo foo)
    {
        foo.Bar = this;
        Foos.Add(foo);
    }
}

映射:

public class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        Id(x => x.Id).UnsavedValue(0).GeneratedBy.Identity();
        Map(x => x.DateField);
        Map(x => x.BoolField);
        Map(x => x.StringField);
        References(x => x.Gorp)
          .Column("GorpId")
          .Class<Gorp>();
        References(x => x.Bar)
          .Column("BarId")
          .Not.Nullable();
    }
}

public class BarMap : ClassMap<Bar>
{
    Id(x => x.Id).UnsavedValue(0).GeneratedBy.Identity();
    Map(x => x.DateField);
    Map(x => x.StringField);

    HasMany<Foo>(x => x.Foos)
      .AsBag()
      .Cascade.SaveUpdate()
      .ForeignKeyConstraintName("FK_Foo_Bar")
      .Inverse()
      .KeyColumn("BarId")
      .Not.KeyNullable();
}

生成的 XML:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
    <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="MyApp.Domain.Model.Bar, MyApp.Domain, Version=3.5.0.0, Culture=neutral, PublicKeyToken=null" table="`Bar`">
        <id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
            <column name="Id" />
            <generator class="identity" />
        </id>
        <property name="DateField" type="System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="DateField" />
        </property>
        <property name="StringField" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="StringField" />
        </property>
        <bag cascade="save-update" inverse="true" name="Foos" mutable="true">
            <key foreign-key="FK_Foo_Bar" not-null="true">
                <column name="BarId" />
            </key>
            <one-to-many class="MyApp.Domain.Model.Foo, MyApp.Domain, Version=3.5.0.0, Culture=neutral, PublicKeyToken=null" />
        </bag>
    </class>
</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-access="property" auto-import="true" default-cascade="none" default-lazy="true">
    <class xmlns="urn:nhibernate-mapping-2.2" mutable="true" name="MyApp.Domain.Model.Foo, MyApp.Domain, Version=3.5.0.0, Culture=neutral, PublicKeyToken=null" table="`Foo`">
        <id name="Id" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" unsaved-value="0">
            <column name="Id" />
            <generator class="identity" />
        </id>
        <property name="DateField" type="System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="DateField" />
        </property>
        <property name="BoolField" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
            <column name="BoolField" />
        </property>
        <property name="StringField" type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
             <column name="StringField" />
        </property>
        <many-to-one class="MyApp.Domain.Model.Gorp, MyApp.Domain, Version=3.5.0.0, Culture=neutral, PublicKeyToken=null" name="Gorp">
            <column name="GorpId" />
        </many-to-one>
        <many-to-one class="MyApp.Domain.Model.Bar, MyApp.Domain, Version=3.5.0.0, Culture=neutral, PublicKeyToken=null" insert="false" name="Bar" update="false">
            <column name="BarId" not-null="true" />
        </many-to-one>
    </class>
</hibernate-mapping>

例外:

无法插入:[MyApp.Domain.Model.Foo][SQL: INSERT INTO [Foo] (DateField, BoolField, StringField, GorpId, BarId) VALUES (?, ?, ?, ?, ?);选择 SCOPE_IDENTITY()]

我错过了什么/误解了什么?

【问题讨论】:

  • 如果我在两边都设置 Cascade.None()(目的是保存 Bar 的实例,在 Foo obj 上设置 BarId,然后保存 Foo 的实例)...我收到一个异常,“SqlDateTime 溢出。必须在 1/1/1753 12:00:00 AM 和 12/31/9999 11:59:59 PM 之间。”,当我尝试保存 Bar 的实例时,因为它试图级联无论如何保存 (??) 并且 Bar.Foos 是空的。我相信它会抛出异常,因为 Foo.DateField 不包含值。

标签: fluent-nhibernate fluent-nhibernate-mapping


【解决方案1】:

谢谢你,戴夫,为我指明了正确的方向。数据库中的一个日期字段可以为空,而另一个则不是。我改变了我的班级和我的地图以反映这一点。瞧!成功了!

太糟糕了,原来的错误消息没有告诉我日期时间字段有问题,这会让我有些头疼。

我做了以下更改:

public class Foo
{
    public virtual int Id { get; set; }
    public virtual Bar Bar{ get; set; }
    public virtual DateTime DateField{ get; set; }
    public virtual string StringField{ get; set; }
    public virtual Gorp Gorp{ get; set; }
    public virtual bool BoolField{ get; set; }

    public Foo()
    {
        DateField = new DateTime(1753, 1, 1);
    }
}

public class Bar
{
    public virtual int Id { get; set; }
    public virtual DateTime? DateField{ get; set; }
    public virtual string StringField{ get; set; }

    public virtual ICollection<Foo> Foos{ get; set; }

    public Bar()
    {
        DateField = new DateTime(1753, 1, 1);
        Foos= new List<Foo>();
    }

    public virtual void AddFoo(Foo foo)
    {
        foo.Bar = this;
        Foos.Add(foo);
    }
}


public class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        Id(x => x.Id).UnsavedValue(0).GeneratedBy.Identity();
        Map(x => x.DateField);
        Map(x => x.BoolField);
        Map(x => x.StringField);
        References(x => x.Gorp)
          .Column("GorpId")
          .Class<Gorp>();
        References(x => x.Bar)
          .Column("BarId")
          .Not.Nullable();
    }
}

public class BarMap : ClassMap<Bar>
{
    Id(x => x.Id).UnsavedValue(0).GeneratedBy.Identity();
    Map(x => x.DateField)
          .Nullable();
    Map(x => x.StringField);

    HasMany<Foo>(x => x.Foos)
      .AsBag()
      .Cascade.SaveUpdate()
      .Fetch.Join()
      .ForeignKeyConstraintName("FK_Foo_Bar")
      .Inverse()
      .KeyColumn("BarId")
      .Not.KeyNullable()
      .Table("Foo");
}

【讨论】:

  • 恭喜修复!如果可以,请确保将您的答案标记为“已接受”,以便其他人看到您的问题已得到解答并能够从您的解决方案中学习。干杯~
【解决方案2】:

如果在创建新对象时没有显式初始化属性,它将接收默认值。对于日期时间,默认值为 datetime.minvalue。

datetime.minvalue 是比 sql server 允许的更早的日期。 .net 和 sql server 不支持相同的日期时间范围,这是令人厌恶的,但事实就是如此。当您尝试将 datetime.minvalue 插入 sql server 时,您会看到您看到的异常。

解决方法是始终为该属性赋予新对象一个值,在数据库中为该列创建一个默认值,或者使 datatime 属性可以为空(DateTime?)。

这实际上与您的类的关系没有任何关系,只是与使用默认值的非初始化不可空日期时间属性有关。

【讨论】:

  • 对,但是为什么当我在 Bar 上调用 Save() 时它甚至试图保存 Foo 对象?如果不是级联,这甚至不应该是一个问题......还是我误解了什么?
  • 我无法投票,因为我还没有 15 个代表,但如果可以的话,我会的。这个答案为我指明了正确的方向。谢谢,戴夫。
  • 没有看到更多代码准确显示您在做什么,我无法提供更多信息。是的,设置级联无应该防止级联。如果您尝试保存一个尚未持久化的对象,该对象引用了另一个尚未持久化的对象而没有禁用级联,您应该会收到一个异常,指出该对象引用了会话不知道的对象。
猜你喜欢
  • 2011-10-24
  • 2011-11-15
  • 2021-11-11
  • 1970-01-01
  • 1970-01-01
  • 2012-07-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多