views:

1899

answers:

5

I'm mapping a objectified relationship (the many->many mapping table contains properties), following this guide.

The sql generated(see exception) is working and returns what i want(strictly speaking it should have been an inner join?). But i get a GenericADOException saying:

could not initialize a collection: [Questionnaires.Core.Questionnaire.Questions#CBDEDAFC183B4CD7AF1422423A91F589][SQL: SELECT questions0_.ida_questionnaire_id as ida4_2_, questions0_.ida_questionnaire_question_id as ida1_2_, questions0_.ida_questionnaire_question_id as ida1_5_1_, questions0_.question_no as question2_5_1_, questions0_.ida_question_id as ida3_5_1_, questions0_.ida_questionnaire_id as ida4_5_1_, question1_.ida_question_id as ida1_3_0_, question1_.ida_question_type_id as ida2_3_0_, question1_.description as descript3_3_0_, question1_.validate_max as validate4_3_0_, question1_.validate_min as validate5_3_0_ FROM ida_questionnaire_question questions0_ left outer join ida_question question1_ on questions0_.ida_question_id=question1_.ida_question_id WHERE questions0_.ida_questionnaire_id=?]

I'm guessing this is because Questionnaire is really mapping to QuestionnaireQuestion not Question(Questionnaire->hasMany->QuestionnaireQuestion<-hasMany<-Question). But cant seem to find a way of going around this.

Question:

public class Question : PersistentObjectWithTypedId<string>
{
    #region Constructors

    public Question()
    {
        Alternatives = new List<Alternative>();
        Questionnaires = new List<Questionnaire>();
    }
    public Question(string description)
        : this()
    {
        Check.Require(!string.IsNullOrEmpty(description) && description.Trim() != string.Empty);
        Description = description;
    }

    #endregion

    #region Properties

    public virtual string Type { get; set; }
    public virtual string Description { get; set; }
    public virtual int Order { get; set; }
    public virtual IList<Questionnaire> Questionnaires { get; set; }
    public virtual IList<Alternative> Alternatives { get; set; }
    public virtual Validator MyValidator { get; set; }
}

public class QuestionMap : ClassMap<Question>
{
    public QuestionMap()
    {
        WithTable("ida_question");
        Id(x => x.ID, "ida_question_id").WithUnsavedValue(0).GeneratedBy.UuidHex("");
        Map(x => x.Description, "description").AsReadOnly();
        Map(x => x.Type, "ida_question_type_id").AsReadOnly();

        Component<Core.Validator>(x => x.MyValidator, m =>
            {
                m.Map(x => x.Type, "ida_question_type_id");
                m.Map(x => x.RangeMin, "validate_min");
                m.Map(x => x.RangeMax, "validate_max");
            });

        HasMany<QuestionnaireQuestion>(x => x.Questionnaires)
            .Cascade.AllDeleteOrphan()
            .WithKeyColumn("ida_question_id");

        HasMany<Alternative>(x => x.Alternatives)
            .IsInverse()
            .WithKeyColumn("ida_question_id")
            .AsBag().SetAttribute("cascade", "all");
    }
}

QuestionnaireQuestion:

public class QuestionnaireQuestion : PersistentObjectWithTypedId<string>
{
 protected QuestionnaireQuestion()
 {
 }

 public virtual int QuestionOrder { get; set; }
    public virtual Question Question {get;set;}
    public virtual Questionnaire Questionnaire { get; set; }
}

public class QuestionnaireQuestionMap : ClassMap<QuestionnaireQuestion>
{
    public QuestionnaireQuestionMap()
    {
        WithTable("ida_questionnaire_question");
        SetAttribute("lazy", "false");
        Id(x => x.ID, "ida_questionnaire_question_id")
            .WithUnsavedValue(0)
            .GeneratedBy.UuidHex("");

        References(x => x.Question, "ida_question_id")
            .WithForeignKey("ida_question_id")
            .FetchType.Join();

        References(x => x.Questionnaire, "ida_questionnaire_id")
            .WithForeignKey("ida_questionnaire_id")
            .FetchType.Join();

        Map(x => x.QuestionOrder, "question_no");
    }
}

Questionnaire:

public class Questionnaire : PersistentObjectWithTypedId<string>
{
    #region Constructors

    public Questionnaire()
    {
        Questions = new List<Question>();
    }
    public Questionnaire(string description) : this()
    {
        Check.Require(!string.IsNullOrEmpty(description) && description.Trim() != string.Empty);
        Description = description;
    }

    #endregion

    #region Properties

    public virtual string Description { get; set; }
    public virtual IList<Question> Questions { get; set; }

    #endregion
}

public class QuestionnaireMap : ClassMap<Questionnaire>
{
    public QuestionnaireMap(){
        WithTable("ida_questionnaire");
        SetAttribute("lazy", "false");
        Id(x => x.ID, "ida_questionnaire_id")
            .WithUnsavedValue(0)
            .GeneratedBy.UuidHex("");

        Map(x => x.Description);

        HasMany<QuestionnaireQuestion>(x => x.Questions)
            .Cascade.AllDeleteOrphan()
            .WithKeyColumn("ida_questionnaire_id");
    }
}
A: 

Are you sure you receive an ADO.NET Exception ? Can you execute the generated query against your database ?

I don't understand your situation however, what is this QuestionairreQuestion class ? You're not using it in your Question class ? I understand that you want to have an order in which your questions should occur.
So, if you have extra properties , I think you should use the QuestionairreQuestion class in your Question class. But, if you only have this QuestionairreQuestion class in order to enforce an 'order' (and no other attributes), then, you could maybe use another Collection-Type instead of an IList.
There are collections in NHibernate which allow you to have ordered-collections (a map , for instance). So, you could use this collection in your Question class to hold Questionairre objects.

(ps: nhibernate generates an outer join, because you didn't specify in your mapping that the collection should not be null. So, an outer join is used because in some circumstances, it could be possible that you have a Question without Questionaires, as far as nhibernate is concerned).

Frederik Gheysels
A: 

The result of running the sql in the exception in the db is 6 rows(the expected number) containg:

  • IDA4_2_: Guids
  • IDA1_2_: Guids
  • IDA1_5_1_: Guids
  • QUESTION2_5_1: Number (order property)
  • IDA3_5_1_: Guids
  • IDA4_5_1: Guids
  • IDA1_3_0_: Guids
  • IDA2_3_0_: MOBILEPHONE (type property)
  • DESCRIPT3_3_0_: mobiltelefon (Description property)
  • VALIDATE4_3_0_: (RangeMin property)
  • VALIDATE5_3_0_: (RangeMax property)

The complete exception is:

[InvalidCastException: Cant use object type Questionnaires.Core.QuestionnaireQuestion as Questionnaires.Core.Question.] NHibernate.Collection.Generic.PersistentGenericBag`1.ReadFrom(IDataReader reader, ICollectionPersister persister, ICollectionAliases descriptor, Object owner) +160 NHibernate.Loader.Loader.ReadCollectionElement(Object optionalOwner, Object optionalKey, ICollectionPersister persister, ICollectionAliases descriptor, IDataReader rs, ISessionImplementor session) +407 NHibernate.Loader.Loader.ReadCollectionElements(Object[] row, IDataReader resultSet, ISessionImplementor session) +412 NHibernate.Loader.Loader.GetRowFromResultSet(IDataReader resultSet, ISessionImplementor session, QueryParameters queryParameters, LockMode[] lockModeArray, EntityKey optionalObjectKey, IList hydratedObjects, EntityKey[] keys, Boolean returnProxies) +472 NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +1010 NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +114 NHibernate.Loader.Loader.LoadCollection(ISessionImplementor session, Object id, IType type) +362

[GenericADOException: could not initialize a collection: [Questionnaires.Core.Questionnaire.Questions#CBDEDAFC183B4CD7AF1422423A91F589][SQL: SELECT questions0_.ida_questionnaire_id as ida4_2_, questions0_.ida_questionnaire_question_id as ida1_2_, questions0_.ida_questionnaire_question_id as ida1_5_1_, questions0_.question_no as question2_5_1_, questions0_.ida_question_id as ida3_5_1_, questions0_.ida_questionnaire_id as ida4_5_1_, question1_.ida_question_id as ida1_3_0_, question1_.ida_question_type_id as ida2_3_0_, question1_.description as descript3_3_0_, question1_.validate_max as validate4_3_0_, question1_.validate_min as validate5_3_0_ FROM ida_questionnaire_question questions0_ left outer join ida_question question1_ on questions0_.ida_question_id=question1_.ida_question_id WHERE questions0_.ida_questionnaire_id=?]] NHibernate.Loader.Loader.LoadCollection(ISessionImplementor session, Object id, IType type) +528 NHibernate.Loader.Collection.CollectionLoader.Initialize(Object id, ISessionImplementor session) +74 NHibernate.Persister.Collection.AbstractCollectionPersister.Initialize(Object key, ISessionImplementor session) +59 NHibernate.Event.Default.DefaultInitializeCollectionEventListener.OnInitializeCollection(InitializeCollectionEvent event) +573 NHibernate.Impl.SessionImpl.InitializeCollection(IPersistentCollection collection, Boolean writing) +150 NHibernate.Collection.AbstractPersistentCollection.ForceInitialization() +287 NHibernate.Engine.StatefulPersistenceContext.InitializeNonLazyCollections() +213 NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +171 NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister) +493 NHibernate.Loader.Entity.AbstractEntityLoader.Load(ISessionImplementor session, Object id, Object optionalObject, Object optionalId) +82 NHibernate.Loader.Entity.AbstractEntityLoader.Load(Object id, Object optionalObject, ISessionImplementor session) +54 NHibernate.Persister.Entity.AbstractEntityPersister.Load(Object id, Object optionalObject, LockMode lockMode, ISessionImplementor session) +206 NHibernate.Event.Default.DefaultLoadEventListener.LoadFromDatasource(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +133 NHibernate.Event.Default.DefaultLoadEventListener.DoLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +948 NHibernate.Event.Default.DefaultLoadEventListener.Load(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +436 NHibernate.Event.Default.DefaultLoadEventListener.ProxyOrLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +236 NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType) +1303 NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType) +125 NHibernate.Impl.SessionImpl.Get(String entityName, Object id) +145 NHibernate.Impl.SessionImpl.Get(Type entityClass, Object id) +66 NHibernate.Impl.SessionImpl.Get(Object id) +91 SharpArch.Data.NHibernate.RepositoryWithTypedId2.Get(IdT id) +152 Questionnaires.Controllers.QuestionnaireController.Create(String username, String id) in C:\Documents and Settings\berbor\Mine dokumenter\Visual Studio 2008\Projects\Questionnaires\Questionnaires.Controllers\QuestionnaireController.cs:40 lambda_method(ExecutionScope , ControllerBase , Object[] ) +205 System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17 System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +178 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) +24 System.Web.Mvc.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7() +52 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func1 continuation) +254 System.Web.Mvc.<>c__DisplayClassc.b__9() +19 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList1 filters, ActionDescriptor actionDescriptor, IDictionary2 parameters) +192 System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +350 System.Web.Mvc.Controller.ExecuteCore() +110 System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +27 System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7 System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +119 System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) +41 System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) +7 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

Bernt
A: 

As for the reason Im' doing this:

One Questionnaire may have many Questions, One Question may have many Questionnaire. I want to be able to give Questions some properties dependent on context(which questionnaire its in), one question may be required in one questionnaire but not another etc. Its more than just order, that was just an example trying to keep it simple.

Bernt
+1  A: 

Then, you should not have a List of Questionnaires in your Question class, but a List of QuestionnaireQuestion objects.

Frederik Gheysels
Thank you.This worked when i also changed my Questionnaire.Questions to Questionnaires.QuestionnaireQuestion.
Bernt
A: 

OK! Thanks for pointing that out.

But that doesn't solve my problem, the exception is when trying to load data into Questionnaire.IList<Question>. And it's still thrown after i modify my IList<Questionnaire> to IList<QuestionnaireQuestion> and the respective mappings.

The problem then would be that Questionnaire.Questions is mapped as a containing QuestionnaireQuestion but is of type Question. But if i try to map it as containing QuestionnaireQuestion my sql does no join at all and it still fails. Is this what i should do, just that I'm doing it wrong? In the example i read he does it like i did as far as i can see, only difference is the newer version of FluentNhibernate that now uses Generics. So i need to specify the correct type.

Bernt

related questions