【问题标题】:Invalid index n for this SqlParameterCollection with Count=n此 SqlParameterCollection 的索引 n 无效,Count=n
【发布时间】:2012-02-14 23:11:25
【问题描述】:

NHibernate 引发异常:“此 SqlParameterCollection 的索引 n 无效,Count=n。”当我尝试保存 Meter 对象时。问题在于 CurrentReading 属性。如果我评论该映射,一切都很好。此外,如果我在数据库 NHibernate 中手动设置 CurrentReading ,则可以正常查询。

我对 NHibernate 比较陌生,任何帮助将不胜感激。

我有以下课程:

public class Meter
{
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }

    public virtual MeterReading CurrentReading { get; protected set; }

    private IList<MeterReading> _readings;
    public virtual ReadOnlyCollection<MeterReading> Readings
    {
        get { return new ReadOnlyCollection<MeterReading>(_readings); }
    }

    public virtual void AddMeterReading(MeterReading MeterReading)
    {
        _readings.Add(MeterReading);
        if (CurrentReading == null || MeterReading.ReadingTimestamp > CurrentReading.ReadingTimestamp)
        {
            CurrentReading = MeterReading;
        }
    }
}

public class MeterReading
{
    public virtual Guid Id { get; set; }
    public virtual decimal Usage { get; set; }
    public virtual DateTime ReadingTimestamp { get; set; }
}

还有这个流畅的 nhibernate 映射文件:

public sealed class MeterMap : ClassMap<Meter>
{
    public MeterMap()
    {
        Id(x => x.Id);

        References(m => m.CurrentReading)
            .Column("CurrentMeterReadingId")
            .LazyLoad()
            .Cascade.None()
            .Nullable();

        HasMany(x => x.Readings)
            .KeyColumn("MeterId")
            .Access.CamelCaseField(Prefix.Underscore)
            .LazyLoad()
            .Inverse()
            .Cascade.All();
    }
}

public sealed class MeterReadingMap : ClassMap<MeterReading>
{
    public MeterReadingMap()
    {
        Id(x => x.Id);

        Map(x => x.ReadingTimestamp)
            .Not.Nullable();

        Map(x => x.Usage)
            .Precision(18)
            .Scale(8)
            .Not.Nullable();
    }
}

【问题讨论】:

    标签: nhibernate fluent-nhibernate


    【解决方案1】:

    我的猜测是它与两次映射同一列有关。这就是此错误的一般含义。您可以尝试这种替代映射,因为您没有在 CurrentMeterReading 上使用级联:

    References(m => m.CurrentReading)
            .Column("CurrentMeterReadingId")
            .Not.Update()
            .Not.Insert();
            .Nullable();
    

    另外我相信延迟加载是默认启用的,所以我认为没有必要在映射中指定它。

    【讨论】:

      【解决方案2】:

      我遇到了同样的问题。我的错误是;我有一个关联属性(多对一),并且在我的映射中没有为该关联指定 insert="false"

      【讨论】:

        【解决方案3】:

        也许为时已晚(2012 年发布的问题),但可能对其他人有用,我遇到了类似的问题。我没有使用 Fluent 映射,但我使用的是 hbm 文件。 据我所见,您有一个带有读数列表的仪表。您已将此 List 属性标记为 Inverse,这很好。 但我的理解是,至少对于 hbm 映射文件,您必须在子项上添加一个“父”属性(在您的示例中为 Reading)以指向父项(在您的示例中为 Meter)。

        这是我的父对象(称为约束):

            <list name="Values" access="field.camelcase-underscore" table="[ConstraintValue]" cascade="all-delete-orphan" inverse="true">
                <key column="ConstraintId" not-null="true"/>
                <list-index column="OrderId" />
                <one-to-many class="ConstraintValue" />
            </list>
        

        这是我的子对象(称为约束值):

            <!--Parent-->
            <many-to-one name="Constraint" access="field.camelcase-underscore" class="Constraint" column="ConstraintId" cascade="merge" not-null="true"/>
        

        同样重要的是要记住,在代码中,不能只向父级添加子级:

        Meter.Readings.Add(newReading);
        

        您还必须设置孩子的父属性:

        newReading.Meter = theParentMeter;
        

        在我自己的示例中,我在父类中有一个 Add 方法来为我做这件事(将集合属性设置为只读以强制代码用户调用此 Add 方法,确保父类始终设置):

        public virtual void AddConstraintValue(ConstraintValue cValue)
            {
                cValue.Constraint = this;
                _values.Add(cValue);
            }
        

        回到最初的问题,我从 Nhibernate 2.2 直接升级到 NH 3.3.3.4001 并开始收到错误“Invalid index n for this SqlParameterCollection with Count=n”。我使用 NH 源代码进行调试,发现 AbstractEntityPersister Dehydrate 方法通过属性并将它们设置在 sql 命令@p0、@p1 等...然后将 Id 添加为最后一个参数。我使用的是hilo id生成,所以id是提前知道的。

        最初,我在父端没有 inverse=true。 Dehydrate 正在尝试设置 sql 命令:

        INSERT INTO [ConstraintValue] (ParameterId, Value, ConstraintId, OrderId, Id) VALUES (@p0, @p1, @p2, @p3, @p4)
        

        但不知何故,@p0 和 @p3 被设置为相同的 hilo Id,我预计 @p3 是列表的“顺序”(0、1 或 2 之类的数字),然后它会尝试设置对象的实际 ID,如 index=5,我得到了异常。

        然后,我终于意识到我错过了 inverse=true,并且在更正后,我在 Dehydrate 中得到了这个 sql 命令:

        INSERT INTO [ConstraintValue] (ConstraintId, ParameterId, Value, Id) VALUES (@p0, @p1, @p2, @p3)
        

        而这一次,属性数组中只有 3 个属性(@p0、@p1 和@p2),在这个阶段甚至没有提到“OrderId”。这 3 个属性在 Dehydrate 的 for 循环中设置,然后在 for 循环之后通过 IdentifierType.NullSafeSet 将 @p3 设置为对象的 Id。

        总而言之,我得到这个异常不是因为我两次映射相同的列或属性,而是因为我有一个父/子关系,而父级上没有 inverse=true。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-12-20
          • 2011-06-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-28
          • 1970-01-01
          相关资源
          最近更新 更多