问题
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.PersistentGenericBag
1[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.
回答1:
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.
来源:https://stackoverflow.com/questions/21445775/fluent-nhibernate-list-of-objects-exception