0

I have a DAL using Fluent NHibernate for mapping a query (stored in db as strings) and their parameters (stored in a separate table).

When I try to use this list of parameters in the service layer, I run into problems.

List<QueryParameter> lqp = (List<QueryParameter>)qry.parameters;

Throws

Unable to cast object of type Hibernate.Collection.Generic.PersistentGenericBag1[nha.cs.utility.mole.QueryParameter]' to type 'System.Collections.Generic.List1 [nha.cs.utility.mole.QueryParameter]'.


List<QueryParameter> lqp = qry.parameters.ToList<QueryParameter>();

Throws

Initializing[nha.cs.utility.mole.Query#24]-failed to lazily initialize a collection of role: nha.cs.utility.mole.Query.parameters, no session or session was closed


List<QueryParameter> lqp = new List<QueryParameter>(qry.parameters);

Throws

Test method MoleSVSTest.MoleSVCTester.MoleSVCTestMethod threw exception: NHibernate.LazyInitializationException:
Initializing[nha.cs.utility.mole.Query#24]-failed to lazily initialize a collection of role: nha.cs.utility.mole.Query.parameters, no session or session was closed


IList<QueryParameter> lqp = (IList<QueryParameter>)qry.parameters;

Throws

Test method MoleSVSTest.MoleSVCTester.MoleSVCTestMethod threw exception: NHibernate.LazyInitializationException: Initializing[nha.cs.utility.mole.Query#24]-failed to lazily initialize a collection of role: nha.cs.utility.mole.Query.parameters, no session or session was closed

public class Query
{
    public virtual int id { get; protected set; }
    public virtual string name { get; set; }
    public virtual string query { get; set; }
    public virtual IList<QueryParameter> parameters { get; set; }
    public virtual IList<Application> applicationsUsedIn { get; set; }

    public Query()
    {
        this.parameters = new List<QueryParameter>();
        this.applicationsUsedIn = new List<Application>();
    }

    public virtual void AddParameter(QueryParameter qp)
    {
        qp.query = this;
        this.parameters.Add(qp);
    }
}


public class QueryMap : ClassMap<Query>
{
    public QueryMap()
    {
        Table("dbo.Queries");
        Id(x => x.id);
        Map(x => x.name);
        Map(x => x.query);
        HasMany(x => x.parameters)
            .Cascade.All()
            .KeyColumn("qryid")
            .LazyLoad()
            ;
        HasManyToMany(x => x.applicationsUsedIn)
            .Table("dbo.ApplicationsQueries")
            .ParentKeyColumn("qryid")
            .ChildKeyColumn("appid")
            .Inverse()
            .LazyLoad()
            ;
    }
}


    public XmlNode runQuery(string appnname, string qryname, List<String> parms)
    {
        XmlNode xn = null;

        if ((null != appnname) && (appnname.Length > 0))
        {
            if ((null != qryname) && (qryname.Length > 0))
            {
                Query qry = md.getQuery(appnname, qryname);
                if (null != qry)
                {
                    if ((null != parms) && (parms.Count > 0)) //Passed parameters as List<String>
                    {
                        //These are the three lines I have tried
                        IList<QueryParameter> lqp = (IList<QueryParameter>)qry.parameters;
                        List<QueryParameter> lqp = qry.parameters.ToList<QueryParameter>();
                        List<QueryParameter> lqp = new List<QueryParameter>(qry.parameters);
    ...
    ...
    ...


Updated with QueryParameter class and map.

public class QueryParameter
{
    public virtual int id { get; set; }
    public virtual Query query { get; set; }
    public virtual string name { get; set; }
    public virtual MOLEDataTypes type { get; set; }
    public virtual int order { get; set; }

    public QueryParameter()
    {
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        var t = obj as QueryParameter;
        if (t == null)
            return false;
        if (query == t.query && name == t.name)
            return true;
        return false;
    }

    public override int GetHashCode()
    {
        return (query.id + "|" + name).GetHashCode();
    }
}


public class QueryParametersMap : ClassMap<QueryParameter>
{
    public QueryParametersMap()
    {
        Table("dbo.QueryParameters");
        Id(x => x.id);
        References(x => x.query).Column("qryid").Not.Insert();
        Map(x => x.name);
        Map(x => x.type).CustomType<MOLEDataTypes>();
        Map(x => x.order).Column("ordr");
    }
}

More Code

    public Query getQuery(string appname, string qryname)
    {
        Query retq = null;

        using (ISessionFactory isf = getSessionFactory())
        {
            using (var sess = isf.OpenSession())
            {
                using (var tran = sess.Transaction)
                {
                    try
                    {
                        tran.Begin();
                        ...
                        USING session
                        ...
                    }
                    catch (Exception ex)
                    {
                        tran.Rollback();
                        sess.Close();
                        lws.logMessage(AppName, "getQuery", ex.ToString(), MessageType.Error, MessageLevel.Error);
                    }
                }
            }
        }

        return (retq);
    }

Any hints or suggestions you might have would be greatly appreciated.

Thanks,

Bruce.

  • What version of NHibernate are you using? And can you please provide your entity/mapping for `QueryParameter` too? – JW Lim Jan 30 '14 at 02:54
  • I added the classes as you suggested. – Bruce McGuire Jan 30 '14 at 16:07
  • Actually, that wasn't necessary. I'm not sure what I was thinking. Anyway, I believe your problem lies with your session. Do you close/clear your session when you call `md.getQuery()`? Or are you wrapping the `getQuery()` method in a `using (session)` block? If so, then that's most probably the cause of your error. Check this question out: http://stackoverflow.com/questions/1893611/nhibernate-failed-to-lazily-initialize-a-collection-of-role – JW Lim Jan 30 '14 at 16:47
  • I am using a session at the DAL level, opening and closing in the getQuery method. Are you saying that I need to keep that session open for the time that I am using the result of the getQuery method. Which is a Query object. – Bruce McGuire Jan 31 '14 at 18:27
  • I added the getQuery method. Is the way that I am using the session here not correct? – Bruce McGuire Jan 31 '14 at 18:47

1 Answers1

0

The problem is that you close the session in getQuery, and then ask for the list of parameters for your Query object, which NHibernate tries to load and fails due to the absence of a session. To solve your problem, you need to ensure that your session is open throughout the request, or at least remains open until all business transactions have completed.

I would suggest that you take a session-per-request approach. Open a session whenever there's a web request, and close it when the session ends. This can be achieved easily by using the Global.asax file, which you can read about here.

public class Global : System.Web.HttpApplication
{    
    public static ISessionFactory SessionFactory { get; private set; }  

    protected void Application_Start(object sender, EventArgs e)
    {
        // Create a session factory when the application starts.
        // Session factories are expensive to create, and therefore you should create one and use it throughout your application.
        SessionFactory = Fluently.Configure()
                                 .Database(
                                    SQLiteConfiguration.Standard
                                        .UsingFile("firstProject.db")
                                 )
                                 .BuildSessionFactory();
    }

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        // Create a session per request.
        var session = SessionFactory.OpenSession();
        CurrentSessionContext.Bind(session);
    }

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        // Close the session at the end of every request
        var session = CurrentSessionContext.Unbind(SessionFactory);
        session.Dispose();
    }

    protected void Application_End(object sender, EventArgs e)
    {
        // Close the session factory at the end of the application.
        if (SessionFactory != null)
            SessionFactory.Dispose();
    }
}

In the example above, a session factory is created when the application starts, which is recommended because it is costly to create a session factory. Then, a session is created every request, and disposed at the end. By using this approach, you save yourself a lot of work since everything is done automatically.

Then, to use a session in your code, you would just need to call GetCurrentSession(), like so:

var session = Global.SessionFactory.GetCurrentSession();

I hope that helps, and best of luck fiddling with NHibernate. I would suggest you to consider reading the NHibernate 3.0 Cookbook for a better understanding of NHibernate.

JW Lim
  • 1,794
  • 3
  • 21
  • 41
  • Thanks very much for your help. You were correct. Once I altered my code to create and maintain the session in the service layer, the problem was solved. – Bruce McGuire Feb 03 '14 at 16:13