[英]Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET will serialize the session state objects, and as a result
[英]Serialize/deserialize “NHibernate Session”, lazy inicialization error (“StateServer mode” for clustering)
我在我的应用程序中使用了一种“每会话会话”模式实现。 在这种方法中,“NHibernate会话”保留在“HTTP会话”上。 在每个“Http请求”中,“NHibernate会话”在开始时重新连接,在请求结束时断开连接。 “NHibernate会话”在“会话”结束时保存在“HTTP会话”中。 这非常有效。
问题:
现在我正在考虑在“Http Session”上使用“StateServer模式 ”。 因此,“HTTP会话”中的所有对象都必须是可序列化的,包括“NHibernate会话”。
所以我正在做一个概念验证,以验证“NHibernate Session”及其缓存对象的序列化/反序列化是否有效。
“概念证明”是以下单元测试。 目标是让它通过。
码:
/*069*/ [Test]
/*070*/ public void Test()
/*071*/ {
/*072*/ //for inspecting the SQL commands
/*073*/ NhSqlLogCapture hhSqlLogCapture = new NhSqlLogCapture();
/*074*/
/*075*/ MemoryStream ms = new MemoryStream();
/*076*/
/*077*/ ISession sessionBefore = null;
/*078*/ ISession sessionAfter = null;
/*079*/
/*080*/ //querying before the serialization.
/*081*/ try
/*082*/ {
/*083*/ sessionBefore = this.sessionFactory.OpenSession();
/*084*/ sessionBefore.FlushMode = FlushMode.Auto;
/*085*/
/*086*/ hhSqlLogCapture.Enable();
/*087*/
/*088*/ //Querying only 'DetailEtt'
/*089*/ hhSqlLogCapture.SqlStatments.Clear();
/*090*/ ICriteria crt = sessionBefore.CreateCriteria<DetailEtt>();
/*091*/ crt
/*092*/ .SetFetchMode("Master", FetchMode.Select);
/*093*/ IList<DetailEtt> cen1DetailList = crt.List<DetailEtt>();
/*094*/
/*095*/ //BEGIN: Serializing
/*096*/ //also serializing an instance of 'DetailEtt' to verify that keeps only one instance to the same database record.
/*097*/ sessionBefore.Disconnect();
/*098*/ Object[] serializationArray = new object[] { sessionBefore, sessionBefore.Get<DetailEtt>(1) };
/*099*/ BinaryFormatter bf = new BinaryFormatter();
/*100*/ bf.Serialize(ms, serializationArray);
/*101*/ //END: Serializing
/*102*/
/*103*/ //assertions
/*104*/ //Checking the sql command executed.
/*105*/ Assert.AreEqual(1, hhSqlLogCapture.SqlStatments.Count, "hhSqlLogCapture.SqlStatments.Count");
/*106*/ Regex rx = new Regex("(?is).*SELECT.*FROM.*DETAIL.*");
/*107*/ Assert.IsTrue(rx.IsMatch(hhSqlLogCapture.SqlStatments[0]), hhSqlLogCapture.SqlStatments[0]);
/*108*/
/*109*/ hhSqlLogCapture.Disable();
/*110*/ }
/*111*/ finally
/*112*/ {
/*113*/ sessionBefore = null;
/*114*/ }
/*115*/
/*116*/ try
/*117*/ {
/*118*/ //BEGIN: Deserializing
/*119*/ BinaryFormatter bf = new BinaryFormatter();
/*120*/ ms.Seek(0, SeekOrigin.Begin);
/*121*/ Object[] deserializationArray = (Object[])bf.Deserialize(ms);
/*122*/ DetailEtt dtEttDeserialized = (DetailEtt)deserializationArray[1];
/*123*/ sessionAfter = (ISession)deserializationArray[0];
/*124*/ //BEGIN: Deserializing
/*125*/
/*126*/ IDbConnection conn = this.dbProvider.CreateConnection();
/*127*/ conn.Open();
/*128*/ sessionAfter.Reconnect(conn);
/*129*/
/*130*/ //Enabling again because the session loses the reference to the log (I think).
/*131*/ hhSqlLogCapture.Enable();
/*132*/ hhSqlLogCapture.SqlStatments.Clear();
/*133*/
/*134*/ DetailEtt dtEtdSSGet = sessionAfter.Get<DetailEtt>(1);
/*135*/ MasterEtt mtEtd = dtEtdSSGet.Master;
/*136*/ Console.WriteLine(mtEtd.Description);
/*137*/
/*138*/ //assertions
/*139*/ //Checking the sql command executed.
/*140*/ Assert.AreEqual(1, hhSqlLogCapture.SqlStatments.Count, "hhSqlLogCapture.SqlStatments.Count");
/*141*/ Regex rx = new Regex("(?is).*SELECT.*FROM.*MASTER.*");
/*142*/ Assert.IsTrue(rx.IsMatch(hhSqlLogCapture.SqlStatments[0]), hhSqlLogCapture.SqlStatments[0]);
/*143*/ //verify that keeps only one instance to the same database record
/*144*/ Assert.AreSame(dtEttDeserialized, dtEtdSSGet, "verify that keeps only one instance to the same database record");
/*145*/ }
/*146*/ finally
/*147*/ {
/*148*/ sessionAfter.Close();
/*149*/ }
/*150*/ }
测试传递几乎所有东西。 但是在尝试加载“懒惰”的实体时失败了。
错误:
SofPOC.Questions.SerializeSession.SerializeSessionTest.Test:
NHibernate.LazyInitializationException : Initializing[SofPOC.Questions.SerializeSession.Entities.MasterEtt#5]-Could not initialize proxy - no Session. em NHibernate.Proxy.AbstractLazyInitializer.Initialize()
at NHibernate.Proxy.AbstractLazyInitializer.Initialize()
at Spring.Data.NHibernate.Bytecode.LazyInitializer.Invoke(IMethodInvocation invocation) in c:\_prj\spring-net\trunk\src\Spring\Spring.Data.NHibernate21\Data\NHibernate\Bytecode\LazyInitializer.cs:line 101
at Spring.Aop.Framework.AbstractMethodInvocation.Proceed() in c:\_prj\spring-net\trunk\src\Spring\Spring.Aop\Aop\Framework\AbstractMethodInvocation.cs:line 284
at Spring.Aop.Framework.DynamicProxy.AdvisedProxy.Invoke(Object proxy, Object target, Type targetType, MethodInfo targetMethod, MethodInfo proxyMethod, Object[] args, IList interceptors) in c:\_prj\spring-net\trunk\src\Spring\Spring.Aop\Aop\Framework\DynamicProxy\AdvisedProxy.cs:line 208
at DecoratorAopProxy_9872659265c04d36bc9738f2aaddfb08.get_Description()
at SofPOC.Questions.SerializeSession.SerializeSessionTest.Test() in C:\Users\hailtondecastro\lixo\stackoverflow\dotnet\StackoverflowNetPOCs_20120718\src\SofPOC.Net4.NH2.Spring13.2010\Questions\SerializeSession\SerializeSessionTest.cs:line 136
DetailEtt:
[Serializable]
public class DetailEtt
{
private Int32? id;
/// <summary>
/// PK
/// </summary>
public virtual Int32? Id
{
get { return id; }
set { id = value; }
}
private String description;
/// <summary>
/// TODO:
/// </summary>
public virtual String Description
{
get { return description; }
set { description = value; }
}
private MasterEtt master;
/// <summary>
/// TODO:
/// </summary>
public virtual MasterEtt Master
{
get { return master; }
set { master = value; }
}
}
MasterEtt:
[Serializable]
public class MasterEtt
{
private Int32? id;
/// <summary>
/// PK
/// </summary>
public virtual Int32? Id
{
get { return id; }
set { id = value; }
}
private String description;
/// <summary>
/// TODO:
/// </summary>
public virtual String Description
{
get { return description; }
set { description = value; }
}
}
DetailEtt.hbm.xml:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="SofPOC.Questions.SerializeSession.Entities" assembly="SofPOC.Net4.NH2.Spring13">
<class name="DetailEtt" table="DETAIL">
<id name="Id" type="Int32">
<column name="ID" sql-type="INTEGER"></column>
<generator class="assigned"></generator>
</id>
<property name="Description" type="String">
<column name="DESCRIPTION" sql-type="VARCHAR( 100 )"></column>
</property>
<many-to-one name="Master" fetch="select">
<column name="MS_ID" sql-type="INTEGER"></column>
</many-to-one>
</class>
</hibernate-mapping>
MasterEtt.hbm.xml:
<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="SofPOC.Questions.SerializeSession.Entities" assembly="SofPOC.Net4.NH2.Spring13">
<class name="MasterEtt" table="MASTER">
<id name="Id" type="Int32">
<column name="ID" sql-type="INTEGER"></column>
<generator class="assigned"></generator>
</id>
<property name="Description" type="String">
<column name="DESCRIPTION" sql-type="VARCHAR( 100 )"></column>
</property>
</class>
</hibernate-mapping>
问题:
即使在重新连接反序列化的“Hibernate会话”后,我也会遇到“延迟加载错误”。 如何在不必重新附加实体的情况下避免这种“延迟加载错误”?
我正在使用:
完整的来源是: Q11553780.7z
笔记:
编辑:
我发现问题的原因是在NHibernate.Proxy.AbstractLazyInitializer
的类NHibernate.Proxy.AbstractLazyInitializer
中。 字段_session
标记为[NonSerialized]
。 这使得该字段不被序列化。 因此在反序列化后它为空。
看代码:
namespace NHibernate.Proxy
{
[Serializable]
public abstract class AbstractLazyInitializer : ILazyInitializer
{
/// <summary>
/// If this is returned by Invoke then the subclass needs to Invoke the
/// method call against the object that is being proxied.
/// </summary>
protected static readonly object InvokeImplementation = new object();
private object _target = null;
private bool initialized;
private object _id;
[NonSerialized]
private ISessionImplementor _session;
...
编辑2:
问题的原因实际上是属性[NonSerialized],因为当我做出以下“hack”时,测试通过了。 通过反思,我将“_session”的属性从“Private | NotSerialized”更改为“Private”。
黑客:
protected override void OnSetUp()
{
//Hacking "_session"
Type aliType = Type.GetType("NHibernate.Proxy.AbstractLazyInitializer, NHibernate");
FieldInfo fiSession = aliType.GetField("_session", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
FieldInfo fi_m_fieldAttributes =
fiSession.GetType().GetField(
"m_fieldAttributes",
System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance);
// changing it from "Private | NotSerialized" to only "Private"
fi_m_fieldAttributes.SetValue(fiSession, FieldAttributes.Private);
base.OnSetUp();
}
据我所知,有三种选择:
如果你试试1号,请告诉我们它是否有效。 我很想知道会发生什么。 从理论上说它应该有效。
根据@Jeroen的建议,这是我自己的答案:
我选择了@Jeroen给出的第三个建议:“创建自己的ILazyInitializer ......”(它有效)。 示范:
以下是它的制作方式:
SerializeSessionTest.spring.config(更改'MySessionFactory'):
<object id="MySessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate21">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingResources">
<list>
<value>assembly://SofPOC.Net4.NH2.Spring13/SofPOC.Questions.SerializeSession.Entities/MasterEtt.hbm.xml</value>
<value>assembly://SofPOC.Net4.NH2.Spring13/SofPOC.Questions.SerializeSession.Entities/DetailEtt.hbm.xml</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<!--<entry key="connection.provider" value="AcessaDados.NHibernate.Connection.SiefDriverConnectionProvider, AcessaDados"/>-->
<entry key="dialect" value="NHibernate.Dialect.SQLiteDialect"/>
<entry key="connection.driver_class" value="NHibernate.Driver.SQLite20Driver"/>
<entry key="current_session_context_class" value="Spring.Data.NHibernate.SpringSessionContext, Spring.Data.NHibernate21"/>
<entry key="hbm2ddl.keywords" value="none"/>
<entry key="query.startup_check" value="false"/>
<entry key="show_sql" value="true"/>
<entry key="format_sql" value="true"/>
<entry key="use_outer_join" value="true"/>
<entry key="bytecode.provider" value="SofPOC.Questions.SerializeSession.Spring.Data.NHibernate.Bytecode.BytecodeProviderSrlzSupport, SofPOC.Net4.NH2.Spring13"/>
<entry key="proxyfactory.factory_class" value="SofPOC.Questions.SerializeSession.Spring.Data.NHibernate.Bytecode.ProxyFactoryFactorySrlzSupport, SofPOC.Net4.NH2.Spring13"/>
</dictionary>
</property>
</object>
LazyInitializerSrlzSupport.cs(这里有一个支持ISessionImplementor序列化的ILazyInitializer实现):
/// <summary>
/// Here was made ILazyInitializer implementation that supports the ISessionImplementor serialization.
/// </summary>
[Serializable]
public class LazyInitializerSrlzSupport :
global::Spring.Data.NHibernate.Bytecode.LazyInitializer,
global::NHibernate.Proxy.ILazyInitializer,
AopAlliance.Intercept.IMethodInterceptor
{
private static readonly MethodInfo exceptionInternalPreserveStackTrace;
static LazyInitializerSrlzSupport()
{
exceptionInternalPreserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic);
}
/// <summary>
/// TODO:
/// </summary>
/// <param name="entityName"></param>
/// <param name="persistentClass"></param>
/// <param name="id"></param>
/// <param name="getIdentifierMethod"></param>
/// <param name="setIdentifierMethod"></param>
/// <param name="componentIdType"></param>
/// <param name="session"></param>
public LazyInitializerSrlzSupport(
string entityName,
Type persistentClass,
object id,
MethodInfo getIdentifierMethod,
MethodInfo setIdentifierMethod,
IAbstractComponentType componentIdType,
ISessionImplementor session)
:base(entityName,
persistentClass,
id,
getIdentifierMethod,
setIdentifierMethod,
componentIdType,
session)
{
this._sessionSrlzSupport = session;
}
/// <summary>
/// This must be the trick. This will be serialized so that
/// we can load the session in the "dynamic proxy".
/// </summary>
private ISessionImplementor _sessionSrlzSupport;
#region ILazyInitializer Members
public new void Initialize()
{
if (this.Session == null)
{
this.Session = this._sessionSrlzSupport;
}
base.Initialize();
}
public new object GetImplementation()
{
this.Initialize();
return base.Target;
}
#endregion
#region IMethodInterceptor Members
object IMethodInterceptor.Invoke(IMethodInvocation invocation)
{
try
{
MethodInfo methodInfo = invocation.Method;
object returnValue = base.Invoke(methodInfo, invocation.Arguments, invocation.Proxy);
if (returnValue != InvokeImplementation)
{
return returnValue;
}
SafeMethod method = new SafeMethod(methodInfo);
return method.Invoke(this.GetImplementation(), invocation.Arguments);
}
catch (TargetInvocationException ex)
{
exceptionInternalPreserveStackTrace.Invoke(ex.InnerException, new Object[] { });
throw ex.InnerException;
}
}
#endregion
}
BytecodeProviderSrlzSupport.cs:
/// <summary>
/// TODO:
/// </summary>
public class BytecodeProviderSrlzSupport :
global::Spring.Data.NHibernate.Bytecode.BytecodeProvider,
global::NHibernate.Bytecode.IBytecodeProvider
{
private IProxyFactoryFactory proxyFactoryFactory;
/// <summary>
/// TODO:
/// </summary>
/// <param name="listableObjectFactory"></param>
public BytecodeProviderSrlzSupport(IListableObjectFactory listableObjectFactory)
: base(listableObjectFactory)
{
this.proxyFactoryFactory = new ProxyFactoryFactorySrlzSupport();
}
#region IBytecodeProvider Members
IProxyFactoryFactory IBytecodeProvider.ProxyFactoryFactory
{
get { return this.proxyFactoryFactory; }
}
#endregion
}
ProxyFactoryFactorySrlzSupport.cs:
/// <summary>
/// TODO:
/// </summary>
public class ProxyFactoryFactorySrlzSupport :
global::NHibernate.Bytecode.IProxyFactoryFactory
{
#region IProxyFactoryFactory Members
/// <summary>
/// Build a proxy factory specifically for handling runtime lazy loading.
/// </summary>
/// <returns>The lazy-load proxy factory.</returns>
public IProxyFactory BuildProxyFactory()
{
return new ProxyFactorySrlzSupport();
}
///<summary>
///</summary>
public IProxyValidator ProxyValidator
{
get { return new DynProxyTypeValidator(); }
}
#endregion
}
ProxyFactorySrlzSupport.cs:
/// <summary>
/// TODO:
/// </summary>
public class ProxyFactorySrlzSupport :
global::Spring.Data.NHibernate.Bytecode.ProxyFactory
{
private static readonly ILog log = LogManager.GetLogger(typeof(ProxyFactorySrlzSupport));
[Serializable]
private class SerializableProxyFactory : global::Spring.Aop.Framework.ProxyFactory
{
// ensure proxy types are generated as Serializable
public override bool IsSerializable
{
get { return true; }
}
}
public override global::NHibernate.Proxy.INHibernateProxy GetProxy(object id, global::NHibernate.Engine.ISessionImplementor session)
{
try
{
// PersistentClass = PersistentClass.IsInterface ? typeof(object) : PersistentClass
LazyInitializer initializer = new LazyInitializerSrlzSupport(EntityName, PersistentClass,
id, GetIdentifierMethod, SetIdentifierMethod, ComponentIdType, session);
SerializableProxyFactory proxyFactory = new SerializableProxyFactory();
proxyFactory.Interfaces = Interfaces;
proxyFactory.TargetSource = initializer;
proxyFactory.ProxyTargetType = IsClassProxy;
proxyFactory.AddAdvice(initializer);
object proxyInstance = proxyFactory.GetProxy();
return (INHibernateProxy)proxyInstance;
}
catch (Exception ex)
{
log.Error("Creating a proxy instance failed", ex);
throw new HibernateException("Creating a proxy instance failed", ex);
}
}
}
完整的来源是: Q11553780.7z
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.