NHibernate抛出异常:“此SqlParameterCollection的索引n无效,Count = n。”当我尝试保存Meter对象时。问题在于CurrentReading属性。如果我评论该映射,一切都很好。另外,如果我在数据库中手动设置CurrentReading,NHibernate会查询它。
我对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();
}
}
答案 0 :(得分:3)
我的猜测是它与两次映射同一列有关。这就是这个错误通常意味着什么。您可以尝试使用此替代映射,因为您没有在CurrentMeterReading上使用级联:
References(m => m.CurrentReading)
.Column("CurrentMeterReadingId")
.Not.Update()
.Not.Insert();
.Nullable();
另外我认为默认情况下启用了延迟加载,所以我认为不需要在映射中指定它。
答案 1 :(得分:2)
我遇到了同样的问题。我的错误是;我有一个关联属性(多对一),并且在我的映射中没有为该关联指定insert="false"
。
答案 2 :(得分:0)
也许为时已晚(2012年发布的问题),但可能对其他人有用,我也有类似的问题。我没有使用Fluent映射,但我正在使用hbm文件。 从我看到的,你有一个有读数列表的仪表。您已将此List属性标记为Inverse,这很好。 但是我的理解,至少对于hbm映射文件,你必须在子项上添加一个“parent”属性(在你的例子中读取)指向父项(在你的例子中是Meter)。
这是我的父对象(称为Constraint):
<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方法为我做(将collection属性设置为只读以强制代码的用户调用此Add方法,确保父代总是集):
public virtual void AddConstraintValue(ConstraintValue cValue)
{
cValue.Constraint = this;
_values.Add(cValue);
}
回到最初的问题,我从Nhibernate 2.2直接升级到NH 3.3.3.4001并开始得到错误“此SqlParameterCollection的无效索引n,其中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,索引= 5,我得到异常。
然后,我终于意识到我错过了 inverse = true ,一旦纠正,我在Dehydrate中得到了这个sql命令:
INSERT INTO [ConstraintValue] (ConstraintId, ParameterId, Value, Id) VALUES (@p0, @p1, @p2, @p3)
这一次,属性数组中只有3个属性(@ p0,@ p1和@ p2),在这个阶段甚至没有提到“OrderId”。这三个属性在Dehydrate中的for循环中设置,然后在for循环之后,通过IdentifierType.NullSafeSet将@ p3设置为对象的Id。
总结一下,我得到这个例外的原因不是因为我两次映射了相同的列或属性,而是因为我在父级上没有使用inverse = true的父/子关系。