我实施了自定义会话状态模块,并按如下方式添加到应用程序中(web.config):
<system.webServer>
<modules>
<remove name="Session" />
<add name="Session" type="CustomServerModules.StateServer.SessionState.SessionStateModule" preCondition="managedHandler" />
</modules>
</system.webServer>
我的IIS应用程序池设置为Integrated v4.0
我将SessionStateData(确切地说是HttpSessionStateContainer
对象)添加到HttpContext
事件BeginAcquireState
上(根据Microsoft的SessionStateModule):
SessionStateUtility.AddHttpSessionStateToContext(_rqContext, _rqSessionState);
但我的问题是Session
Page
中的对象没有值,它会引发异常:
Session state can only be used when enableSessionState is set to true, either in a configuration file or in the Page directive. Please also make sure that System.Web.SessionStateModule or a custom session state module is included in the <configuration>\\<system.web>\\<httpModules> section in the application configuration.
此外HttpContext.Current.Session
为空。
现在问题是:为什么在自定义模式下应用程序页面中无法访问会话?在创建和启动HttpApplications和Page实例以跟踪Session对象时,如何跟踪IIS和ASP.NET的行为?
更新
我包含了我的Session HttpModule的 AcquireRequest 方式,如下所示。在此部分中,新会话将添加到HttpContext
,Session_Start
将添加:
public sealed class SessionStateModule : IHttpModule
{
/*
* Add a Session_OnStart event handler.
*/
public event EventHandler Start
{
add
{
_sessionStartEventHandler += value;
}
remove
{
_sessionStartEventHandler -= value;
}
}
/*
* IHttpModule Member
*/
public void Init(HttpApplication app)
{
bool initModuleCalled = false;
string applicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
var webConfig = WebConfigurationManager.OpenWebConfiguration(applicationName);
s_sessionWebConfig = (SessionStateSection)webConfig.GetSection("system.web/sessionState");
lck = new ReaderWriterLockSlim();
if (!s_oneTimeInit)
{
lck.EnterWriteLock();
try
{
if (!s_oneTimeInit)
{
initializeModuleFromWebConfig(app);
initModuleCalled = true;
s_Application = app;
s_timeOut = (int)s_sessionWebConfig.Timeout.TotalMinutes;
s_useHostingIdentity = s_sessionWebConfig.UseHostingIdentity; // default value of UseHostingIdentity is true.
s_configExecutionTimeout = ((HttpRuntimeSection)webConfig.GetSection("system.web/httpRuntime")).ExecutionTimeout; // DefaultValue of ExecutionTimeout = "00:01:50"
s_configRegenerateExpiredSessionId = s_sessionWebConfig.RegenerateExpiredSessionId;
s_configCookieless = s_sessionWebConfig.Cookieless;
s_configMode = s_sessionWebConfig.Mode;
// The last thing to set in this if-block.
s_oneTimeInit = true;
}
}
finally
{
lck.ExitWriteLock();
}
}
if (!initModuleCalled)
{
initializeModuleFromWebConfig(app);
}
}
private void initializeModuleFromWebConfig(HttpApplication app)
{
#region registering application (HttpApplication) event handlers
app.AddOnAcquireRequestStateAsync(
new BeginEventHandler(this.app_BeginAcquireState),
new EndEventHandler(this.app_EndAcquireState));
app.ReleaseRequestState += app_ReleaseRequestState;
app.EndRequest += app_EndRequest;
#endregion
#region instantiating SessionStateStoreProvider
string providerName = s_sessionWebConfig.CustomProvider;
ProviderSettings ps;
_store = (SessionStateStoreProviderBase)
ProvidersHelper.InstantiateProvider(ps, typeof(SessionStateStoreProviderBase));
#endregion
_idManager = initSessionIDManager(s_sessionWebConfig);
}
private IAsyncResult app_BeginAcquireState(object source, EventArgs e, AsyncCallback cb, object extraData)
{
bool requiresState;
bool isCompleted = true;
bool skipReadingId = false;
_acquireCalled = true;
_releaseCalled = false;
resetPerRequestFields();
_rqContext = ((HttpApplication)source).Context;
_rqAr = new HttpAsyncResult(cb, extraData);
try
{
/* notifying the store that we are beginning to get process request */
_store.InitializeRequest(_rqContext);
/* determine if the request requires state at all */
requiresState = _rqContext.Handler is IRequiresSessionState; //originally was: _rqContext.RequiresSessionState;
// SessionIDManager may need to do a redirect if cookieless setting is AutoDetect
if (_idManager.InitializeRequest(_rqContext, false, out _rqSupportSessionIdReissue))
{
_rqAr.Complete(true, null, null);
return _rqAr;
}
/* Get sessionid */
_rqId = _idManager.GetSessionID(_rqContext);
if (requiresState)
{
// Still need to update the sliding timeout to keep session alive.
if (_rqId != null)
{
_store.ResetItemTimeout(_rqContext, _rqId);
}
_rqAr.Complete(true, null, null);
return _rqAr;
}
_rqExecutionTimeout = s_configExecutionTimeout;
/* determine if we need just read-only access */
_rqReadonly = _rqContext.Handler is IReadOnlySessionState; // originally was: _rqContext.ReadOnlySessionState;
if (_rqId != null)
{
/* get the session state corresponding to this session id */
isCompleted = getSessionStateItem();
}
else if (!skipReadingId)
{
/* if there's no id yet, create it */
bool redirected = createSessionId();
_rqIdNew = true;
if (redirected)
{
if (s_configRegenerateExpiredSessionId)
{
// See inline comments in CreateUninitializedSessionState()
createUninitializedSessionState();
}
_rqAr.Complete(true, null, null);
// commented because this is in case of inProc and OutOfProc
//if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest);
//return _rqAr;
}
}
if (isCompleted)
{
completeAcquireState(source, e);
_rqAr.Complete(true, null, null);
}
return _rqAr;
}
finally
{
}
}
// Called when AcquireState is done. This function will add the returned
// SessionStateStore item to the request context.
private void completeAcquireState(object source, EventArgs e)
{
if (_rqItem != null)
{
_rqSessionStateNotFound = false;
if ((_rqActionFlags & SessionStateActions.InitializeItem) != 0)
{
//Initialize an uninit item.
_rqIsNewSession = true;
}
else
{
_rqIsNewSession = false;
}
}
else
{
_rqIsNewSession = true;
_rqSessionStateNotFound = true;
// We couldn't find the session state.
if (!_rqIdNew && // If the request has a session id, that means the session state has expired
s_configRegenerateExpiredSessionId && // And we're asked to regenerate expired session
_rqSupportSessionIdReissue)
{
// And this request support session id reissue
// We will generate a new session id for this expired session state
bool redirected = createSessionId();
if (redirected)
{
// Will redirect because we've reissued a new id and it's cookieless
createUninitializedSessionState();
return;
}
}
}
initStateStoreItem(true);
if (_rqIsNewSession)
{
raiseOnStart(source, e);
}
}
private void initStateStoreItem(bool addToContext)
{
try
{
if (_rqItem == null)
{
//Creating new session state
_rqItem = _store.CreateNewStoreData(_rqContext, s_timeOut);
}
_rqSessionItems = _rqItem.Items;
if (_rqSessionItems == null)
{
throw new HttpException("Null Value for SessionStateItemCollection");
}
// No check for null because we allow our custom provider to return a null StaticObjects.
_rqStaticObjects = _rqItem.StaticObjects;
_rqSessionItems.Dirty = false;
_rqSessionState = new HttpSessionStateContainer(
_rqId,
_rqSessionItems,
_rqStaticObjects,
_rqItem.Timeout,
_rqIsNewSession,
s_configCookieless,
s_configMode,
_rqReadonly);
if (addToContext)
{
SessionStateUtility.AddHttpSessionStateToContext(_rqContext, _rqSessionState);
}
}
finally
{
}
}
private void app_EndAcquireState(IAsyncResult ar)
{
((HttpAsyncResult)ar).End();
}
}
以下列出的代码概述:
Init()
方法将实例化商店提供商并添加AcquireRequest
,ReleaseRequest
和EndRequest
个HttpApplication
事件的处理程序。它也实例化SessionIDManger
。app_BeginAcquireState()
将在createUninitializedSessionState()
中创建新的会话项,用于在数据存储中存储新项目。completeAcquireState()
通过调用HttpContext
调用将新会话项添加到initStateStoreItem()
。应用程序的Global.asax Session_Start
也会成功调用此方法。SessionStateUtility.AddHttpSessionStateToContext(_rqContext, _rqSessionState);