Fluent NHibernate - IndexOutOfRange
Asked Answered
R

3

5

I've read all the posts and know that IndexOutOfRange usually happens because a column is being referenced twice. But I don't see how that's happening based on my mappings. With SHOW_SQL true in the config, I see an Insert into the Events table and then an IndexOutOfRangeException that refers to the RadioButtonQuestions table. I can't see the SQL it's trying to use that generates the exception. I tried using AutoMapping and have now switched to full ClassMap for these two classes to try to narrow down the problem.

public class RadioButtonQuestion : Entity
{
    [Required]
    public virtual Event Event { get; protected internal set; }

    [Required]
    public virtual string GroupIntroText { get; set; }
}

public class Event : Entity
{
    [Required]
    public virtual string Title { get; set; }

    [Required]
    public virtual DateTime EventDate { get; set; }

    public virtual IList<RadioButtonQuestions> RadioButtonQuestions { get; protected internal set; }
}




public class RadioButtonQuestionMap : ClassMap<RadioButtonQuestion>
{
    public RadioButtonQuestionMap()
    {
        Table("RadioButtonQuestions");

        Id(x => x.Id).Column("RadioButtonQuestionId").GeneratedBy.Identity();

        Map(x => x.GroupIntroText);
        References(x => x.Event).Not.Nullable();
    }
}


public class EventMap : ClassMap<Event>
{
    public EventMap()
    {
        Id(x => x.Id).Column("EventId").GeneratedBy.Identity();
        Map(x => x.EventDate);
        Map(x => x.Title);
        HasMany(x => x.RadioButtonQuestions).AsList(x => x.Column("ListIndex")).KeyColumn("EventId").Not.Inverse().Cascade.AllDeleteOrphan().Not.KeyNullable();
    }
}

The generated SQL looks correct:

create table Events (
    EventId INT IDENTITY NOT NULL,
   EventDate DATETIME not null,
   Title NVARCHAR(255) not null,
   primary key (EventId)
)

create table RadioButtonQuestions (
    RadioButtonQuestionId INT IDENTITY NOT NULL,
   GroupIntroText NVARCHAR(255) not null,
   EventId INT not null,
   ListIndex INT null,
   primary key (RadioButtonQuestionId)
)

This is using NH 3.3.0.4000 and FNH 1.3.0.727. When I try to save a new Event (with a RadioButtonQuestion attached) I see

NHibernate: INSERT INTO Events (EventDate, Title) VALUES (@p0, @p1);@p0 = 5/21/2012 12:32:11 PM [Type: DateTime (0)], @p1 = 'My Test Event' [Type: String (0)] NHibernate: select @@IDENTITY

Events.Tests.Events.Tasks.EventTasksTests.CanCreateEvent: NHibernate.PropertyValueException : Error dehydrating property value for Events.Domain.RadioButtonQuestion._Events.Domain.Event.RadioButtonQuestionsIndexBackref ----> System.IndexOutOfRangeException : An SqlCeParameter with ParameterIndex '3' is not contained by this SqlCeParameterCollection.

So if a column really is being referenced twice, what's the problem with my FNH config that's causing that behavior? I'm trying for a bidirection relationship (One Event Has Many Radio Button Questions) with ordering (I'll maintain it since NH won't in a bidir relationship, from what I've read). FWIW I also tried this as a unidirectional relationship by removing the Event from RadioButtonQuestion and it still caused the same exception.

Rote answered 21/5, 2012 at 16:35 Comment(2)
FWIW, I can get the exception to go away if I use Inverse() instead of Not.Inverse() but that seems odd... the parent should be managing this, so I would want Not.Inverse(), I thought.Rote
The first sentence of this question was the answer I needed, well worded, thanks!Haughty
M
5

You have a bidirectional association, so one side should be marked as Inverse() and that can only be the RadioButtonQuestions collection. If you want the collection to be the owner, you have to remove the reference to the event in your RadioButtonQuestion class.

Additionally, the EventId column in the table RadioButtonQuestions is not nullable, which can cause problems, if the collection mapping is not inverse. See the note in the documentation.

Myrtlemyrvyn answered 21/5, 2012 at 18:41 Comment(1)
Annoying. I really want the bidirectional relationship... I guess I can live with Inverse() if that's the correct model in this case and re-reading the docs on bidirectional it seems like that's the only option. I wonder why that's the case. Guess NH really doesn't want you using bidir ... you also lose automatic list indexing. As for nullable, my unit tests for create & update are passing, but I'll keep an eye on it.Rote
T
7

I am using mapping in code (NH 3.3.1) and I have noticed that adding Update(false) and Insert(false) cures the problem:

ManyToOne(x => x.DictionaryEntity, map =>
{
    map.Column("Dictionary");
    map.Update(false);
    map.Insert(false);
    map.Cascade(Cascade.None);
    map.Fetch(FetchKind.Select);
    map.NotFound(NotFoundMode.Exception);
    map.Lazy(LazyRelation.Proxy);
});
Tiernan answered 29/8, 2012 at 18:9 Comment(1)
Wonderful. This solved my problem. I was using FluentNHibernate and needed to use: .Not.Update().Not.Insert(), but it solved it nonetheless. Thanks!Jury
M
5

You have a bidirectional association, so one side should be marked as Inverse() and that can only be the RadioButtonQuestions collection. If you want the collection to be the owner, you have to remove the reference to the event in your RadioButtonQuestion class.

Additionally, the EventId column in the table RadioButtonQuestions is not nullable, which can cause problems, if the collection mapping is not inverse. See the note in the documentation.

Myrtlemyrvyn answered 21/5, 2012 at 18:41 Comment(1)
Annoying. I really want the bidirectional relationship... I guess I can live with Inverse() if that's the correct model in this case and re-reading the docs on bidirectional it seems like that's the only option. I wonder why that's the case. Guess NH really doesn't want you using bidir ... you also lose automatic list indexing. As for nullable, my unit tests for create & update are passing, but I'll keep an eye on it.Rote
F
1

I just spent a morning rooting this error out. The IndexOutOfRangeException sent me down the wrong path initially, but I've found the cause. My problem concerned a FluentNHibernate class map that uses several components; the issue was that two properties were inadvertedly and incorrectly mapped to one and the same column:

before:

// example is stripped for simplicity, note the column names
Component(mappedClass => mappedClass.MappedComponent1,
          map => 
          {
              map.Map(c => c.SomeProperty, "samecolumn");
          });

Component(mappedClass => mappedClass.MappedComponent2,
          map => 
          {
              map.Map(c => c.OtherProperty, "samecolumn");
          });

after:

Component(mappedClass => mappedClass.MappedComponent1,
          map => 
          {
              map.Map(c => c.SomeProperty, "firstcolumn");
          });

Component(mappedClass => mappedClass.MappedComponent2,
          map => 
          {
              map.Map(c => c.OtherProperty, "secondcolumn");
          });

How this results in an IndexOutOfRangeException isn't obvious to me; I'm guessing that there's an array of mapped (source) properties and an array of destination columns, and in this case the destination array is too short for the number of items in the source properties array, because some of the destination columns are identical.

I think but it's worth writing a pull request for FluentNHibernate to check for this and throw a more explicit exception.

Forevermore answered 16/10, 2018 at 11:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.