|
//------------------------------------------------------------------------------
// <copyright file="SqlSessionStateStore.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
* SqlSessionStateStore.cs
*
* Copyright (c) 1998-2000, Microsoft Corporation
*
*/
namespace System.Web.SessionState {
using System;
using System.Configuration;
using System.Collections;
using System.Threading;
using System.IO;
using System.Web;
using System.Web.Caching;
using System.Web.Util;
using System.Data;
using System.Data.SqlClient;
using System.Data.Common;
using System.Text;
using System.Security.Principal;
using System.Xml;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Globalization;
using System.Web.Management;
using System.Web.Hosting;
using System.Web.Configuration;
/*
* Provides session state via SQL Server
*/
internal class SqlSessionStateStore : SessionStateStoreProviderBase {
internal enum SupportFlags : uint {
None = 0x00000000,
GetLockAge = 0x00000001,
Uninitialized = 0xFFFFFFFF
}
#pragma warning disable 0649
static ReadWriteSpinLock s_lock;
#pragma warning restore 0649
static int s_isClearPoolInProgress;
static int s_commandTimeout;
static TimeSpan s_retryInterval;
static SqlPartitionInfo s_singlePartitionInfo;
static PartitionManager s_partitionManager;
static bool s_oneTimeInited;
static bool s_usePartition;
static EventHandler s_onAppDomainUnload;
// We keep these info because we don't want to hold on to the config object.
static string s_configPartitionResolverType;
static string s_configSqlConnectionFileName;
static int s_configSqlConnectionLineNumber;
static bool s_configAllowCustomSqlDatabase;
static bool s_configCompressionEnabled;
// Per request info
HttpContext _rqContext;
int _rqOrigStreamLen;
IPartitionResolver _partitionResolver;
SqlPartitionInfo _partitionInfo;
const int ITEM_SHORT_LENGTH = 7000;
const int SQL_ERROR_PRIMARY_KEY_VIOLATION = 2627;
const int SQL_LOGIN_FAILED = 18456;
const int SQL_LOGIN_FAILED_2 = 18452;
const int SQL_LOGIN_FAILED_3 = 18450;
const int SQL_CANNOT_OPEN_DATABASE_FOR_LOGIN = 4060;
const int SQL_TIMEOUT_EXPIRED = -2;
const int APP_SUFFIX_LENGTH = 8;
const int FIRST_RETRY_SLEEP_TIME = 5000;
const int RETRY_SLEEP_TIME = 1000;
static int ID_LENGTH = SessionIDManager.SessionIDMaxLength + APP_SUFFIX_LENGTH;
internal const int SQL_COMMAND_TIMEOUT_DEFAULT = 30; // in sec
internal SqlSessionStateStore() {
}
internal override void Initialize(string name, NameValueCollection config, IPartitionResolver partitionResolver) {
_partitionResolver = partitionResolver;
Initialize(name, config);
}
#if DBG
SessionStateModule _module;
internal void SetModule(SessionStateModule module) {
_module = module;
}
#endif
public override void Initialize(string name, NameValueCollection config)
{
if (String.IsNullOrEmpty(name))
name = "SQL Server Session State Provider";
base.Initialize(name, config);
if (!s_oneTimeInited) {
s_lock.AcquireWriterLock();
try {
if (!s_oneTimeInited) {
OneTimeInit();
}
}
finally {
s_lock.ReleaseWriterLock();
}
}
if (!s_usePartition) {
// For single partition, the connection info won't change from request to request
Debug.Assert(_partitionResolver == null);
_partitionInfo = s_singlePartitionInfo;
}
}
void OneTimeInit() {
SessionStateSection config = RuntimeConfig.GetAppConfig().SessionState;
s_configPartitionResolverType = config.PartitionResolverType;
s_configSqlConnectionFileName = config.ElementInformation.Properties["sqlConnectionString"].Source;
s_configSqlConnectionLineNumber = config.ElementInformation.Properties["sqlConnectionString"].LineNumber;
s_configAllowCustomSqlDatabase = config.AllowCustomSqlDatabase;
s_configCompressionEnabled = config.CompressionEnabled;
if (_partitionResolver == null) {
String sqlConnectionString = config.SqlConnectionString;
SessionStateModule.ReadConnectionString(config, ref sqlConnectionString, "sqlConnectionString");
s_singlePartitionInfo = (SqlPartitionInfo)CreatePartitionInfo(sqlConnectionString);
}
else {
s_usePartition = true;
s_partitionManager = new PartitionManager(new CreatePartitionInfo(CreatePartitionInfo));
}
s_commandTimeout = (int)config.SqlCommandTimeout.TotalSeconds;
s_retryInterval = config.SqlConnectionRetryInterval;
s_isClearPoolInProgress = 0;
// We only need to do this in one instance
s_onAppDomainUnload = new EventHandler(OnAppDomainUnload);
Thread.GetDomain().DomainUnload += s_onAppDomainUnload;
// Last thing to set.
s_oneTimeInited = true;
}
void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) {
Debug.Trace("SqlSessionStateStore", "OnAppDomainUnload called");
Thread.GetDomain().DomainUnload -= s_onAppDomainUnload;
if (_partitionResolver == null) {
if (s_singlePartitionInfo != null) {
s_singlePartitionInfo.Dispose();
}
}
else {
if (s_partitionManager != null) {
s_partitionManager.Dispose();
}
}
}
internal IPartitionInfo CreatePartitionInfo(string sqlConnectionString) {
/*
* Parse the connection string for errors. We want to ensure
* that the user's connection string doesn't contain an
* Initial Catalog entry, so we must first create a dummy connection.
*/
SqlConnection dummyConnection;
string attachDBFilename = null;
try {
dummyConnection = new SqlConnection(sqlConnectionString);
}
catch (Exception e) {
if (s_usePartition) {
HttpException outerException = new HttpException(
SR.GetString(SR.Error_parsing_sql_partition_resolver_string, s_configPartitionResolverType, e.Message), e);
outerException.SetFormatter(new UseLastUnhandledErrorFormatter(outerException));
throw outerException;
}
else {
throw new ConfigurationErrorsException(
SR.GetString(SR.Error_parsing_session_sqlConnectionString, e.Message), e,
s_configSqlConnectionFileName, s_configSqlConnectionLineNumber);
}
}
// Search for both Database and AttachDbFileName. Don't append our
// database name if either of them exists.
string database = dummyConnection.Database;
SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(sqlConnectionString);
if (String.IsNullOrEmpty(database)) {
database = scsb.AttachDBFilename;
attachDBFilename = database;
}
if (!String.IsNullOrEmpty(database)) {
if (!s_configAllowCustomSqlDatabase) {
if (s_usePartition) {
throw new HttpException(
SR.GetString(SR.No_database_allowed_in_sql_partition_resolver_string,
s_configPartitionResolverType, dummyConnection.DataSource, database));
}
else {
throw new ConfigurationErrorsException(
SR.GetString(SR.No_database_allowed_in_sqlConnectionString),
s_configSqlConnectionFileName, s_configSqlConnectionLineNumber);
}
}
if (attachDBFilename != null) {
HttpRuntime.CheckFilePermission(attachDBFilename, true);
}
}
else {
sqlConnectionString += ";Initial Catalog=ASPState";
}
return new SqlPartitionInfo(new ResourcePool(new TimeSpan(0, 0, 5), int.MaxValue),
scsb.IntegratedSecurity,
sqlConnectionString);
}
public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) {
return false;
}
public override void Dispose() {
}
public override void InitializeRequest(HttpContext context) {
Debug.Assert(context != null, "context != null");
_rqContext = context;
_rqOrigStreamLen = 0;
if (s_usePartition) {
// For multiple partition case, the connection info can change from request to request
_partitionInfo = null;
}
}
public override void EndRequest(HttpContext context) {
Debug.Assert(context != null, "context != null");
_rqContext = null;
}
public bool KnowForSureNotUsingIntegratedSecurity {
get {
if (_partitionInfo == null) {
Debug.Assert(s_usePartition, "_partitionInfo can be null only if we're using paritioning and we haven't called GetConnection yet.");
// If we're using partitioning, we need the session id to figure out the connection
// string. Without it, we can't know for sure.
return false;
}
else {
Debug.Assert(_partitionInfo != null);
return !_partitionInfo.UseIntegratedSecurity;
}
}
}
//
// Regarding resource pool, we will turn it on if in <identity>:
// - User is not using integrated security
// - impersonation = "false"
// - impersonation = "true" and userName/password is NON-null
// - impersonation = "true" and IIS is using Anonymous
//
// Otherwise, the impersonated account will be dynamic and we have to turn
// resource pooling off.
//
// Note:
// In case 2. above, the user can specify different usernames in different
// web.config in different subdirs in the app. In this case, we will just
// cache the connections in the resource pool based on the identity of the
// connection. So in this specific scenario it is possible to have the
// resource pool filled with mixed identities.
//
bool CanUsePooling() {
bool ret;
if (KnowForSureNotUsingIntegratedSecurity) {
Debug.Trace("SessionStatePooling", "CanUsePooling: not using integrated security");
ret = true;
}
else if (_rqContext == null) {
// One way this can happen is we hit an error on page compilation,
// and SessionStateModule.OnEndRequest is called
Debug.Trace("SessionStatePooling", "CanUsePooling: no context");
ret = false;
}
else if (!_rqContext.IsClientImpersonationConfigured) {
Debug.Trace("SessionStatePooling", "CanUsePooling: mode is None or Application");
ret = true;
}
else if (HttpRuntime.IsOnUNCShareInternal) {
Debug.Trace("SessionStatePooling", "CanUsePooling: mode is UNC");
ret = false;
}
else {
string logon = _rqContext.WorkerRequest.GetServerVariable("LOGON_USER");
Debug.Trace("SessionStatePooling", "LOGON_USER = '" + logon + "'; identity = '" + _rqContext.User.Identity.Name + "'; IsUNC = " + HttpRuntime.IsOnUNCShareInternal);
if (String.IsNullOrEmpty(logon)) {
ret = true;
}
else {
ret = false;
}
}
Debug.Trace("SessionStatePooling", "CanUsePooling returns " + ret);
return ret;
}
SqlStateConnection GetConnection(string id, ref bool usePooling) {
SqlStateConnection conn = null;
if (_partitionInfo == null) {
Debug.Assert(s_partitionManager != null);
Debug.Assert(_partitionResolver != null);
_partitionInfo = (SqlPartitionInfo)s_partitionManager.GetPartition(_partitionResolver, id);
}
Debug.Trace("SessionStatePooling", "Calling GetConnection under " + WindowsIdentity.GetCurrent().Name);
#if DBG
Debug.Assert(_module._rqChangeImpersonationRefCount != 0,
"SessionStateModule.ChangeImpersonation should have been called before making any call to SQL");
#endif
usePooling = CanUsePooling();
if (usePooling) {
conn = (SqlStateConnection) _partitionInfo.RetrieveResource();
if (conn != null && (conn.Connection.State & ConnectionState.Open) == 0) {
conn.Dispose();
conn = null;
}
}
if (conn == null) {
conn = new SqlStateConnection(_partitionInfo, s_retryInterval);
}
return conn;
}
void DisposeOrReuseConnection(ref SqlStateConnection conn, bool usePooling) {
try {
if (conn == null) {
return;
}
if (usePooling) {
conn.ClearAllParameters();
_partitionInfo.StoreResource(conn);
conn = null;
}
}
finally {
if (conn != null) {
conn.Dispose();
}
}
}
internal static void ThrowSqlConnectionException(SqlConnection conn, Exception e) {
if (s_usePartition) {
throw new HttpException(
SR.GetString(SR.Cant_connect_sql_session_database_partition_resolver,
s_configPartitionResolverType, conn.DataSource, conn.Database),
e);
}
else {
throw new HttpException(
SR.GetString(SR.Cant_connect_sql_session_database),
e);
}
}
SessionStateStoreData DoGet(HttpContext context, String id, bool getExclusive,
out bool locked,
out TimeSpan lockAge,
out object lockId,
out SessionStateActions actionFlags) {
SqlDataReader reader;
byte [] buf;
MemoryStream stream = null;
SessionStateStoreData item;
bool useGetLockAge = false;
SqlStateConnection conn = null;
SqlCommand cmd = null;
bool usePooling = true;
Debug.Assert(id.Length <= SessionIDManager.SESSION_ID_LENGTH_LIMIT, "id.Length <= SessionIDManager.SESSION_ID_LENGTH_LIMIT");
Debug.Assert(context != null, "context != null");
// Set default return values
locked = false;
lockId = null;
lockAge = TimeSpan.Zero;
actionFlags = 0;
buf = null;
reader = null;
conn = GetConnection(id, ref usePooling);
Debug.Assert(_partitionInfo != null, "_partitionInfo != null");
Debug.Assert(_partitionInfo.SupportFlags != SupportFlags.Uninitialized, "_partitionInfo.SupportFlags != SupportFlags.Uninitialized");
//
// In general, if we're talking to a SQL 2000 or above, we use LockAge; otherwise we use LockDate.
// Below are the details:
//
// Version 1
// ---------
// In v1, the lockDate is generated and stored in SQL using local time, and we calculate the "lockage"
// (i.e. how long the item is locked) by having the web server read lockDate from SQL and substract it
// from DateTime.Now. But this approach introduced two problems:
// 1. SQL server and web servers need to be in the same time zone.
// 2. Daylight savings problem.
//
// Version 1.1
// -----------
// In v1.1, if using SQL 2000 we fixed the problem by calculating the "lockage" directly in SQL
// so that the SQL server and the web server don't have to be in the same time zone. We also
// use UTC date to store time in SQL so that the Daylight savings problem is solved.
//
// In summary, if using SQL 2000 we made the following changes to the SQL tables:
// i. The column Expires is using now UTC time
// ii. Add new SP TempGetStateItem2 and TempGetStateItemExclusive2 to return a lockage
// instead of a lockDate.
// iii. To support v1 web server, we still need to have TempGetStateItem and
// TempGetStateItemExclusive. However, we modify it a bit so that they use
// UTC time to update Expires column.
//
// If using SQL 7, we decided not to fix the problem, and the SQL scripts for SQL 7 remain pretty much
// the same. That means v1.1 web server will continue to call TempGetStateItem and
// TempGetStateItemExclusive and use v1 way to calculate the "lockage".
//
// Version 2.0
// -----------
// In v2.0 we added some new SP TempGetStateItem3 and TempGetStateItemExclusive3
// because we added a new return value 'actionFlags'. However, the principle remains the same
// that we support lockAge only if talking to SQL 2000.
//
// (When one day MS stops supporting SQL 7 we can remove all the SQL7-specific scripts and
// stop all these craziness.)
//
if ((_partitionInfo.SupportFlags & SupportFlags.GetLockAge) != 0) {
useGetLockAge = true;
}
try {
if (getExclusive) {
cmd = conn.TempGetExclusive;
}
else {
cmd = conn.TempGet;
}
cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix; // @id
cmd.Parameters[1].Value = Convert.DBNull; // @itemShort
cmd.Parameters[2].Value = Convert.DBNull; // @locked
cmd.Parameters[3].Value = Convert.DBNull; // @lockDate or @lockAge
cmd.Parameters[4].Value = Convert.DBNull; // @lockCookie
cmd.Parameters[5].Value = Convert.DBNull; // @actionFlags
using(reader = SqlExecuteReaderWithRetry(cmd, CommandBehavior.Default)) {
/* If the cmd returned data, we must read it all before getting out params */
if (reader != null) {
try {
if (reader.Read()) {
Debug.Trace("SqlSessionStateStore", "Sql Get returned long item");
buf = (byte[]) reader[0];
}
} catch(Exception e) {
ThrowSqlConnectionException(cmd.Connection, e);
}
}
}
/* Check if value was returned */
if (Convert.IsDBNull(cmd.Parameters[2].Value)) {
Debug.Trace("SqlSessionStateStore", "Sql Get returned null");
return null;
}
/* Check if item is locked */
Debug.Assert(!Convert.IsDBNull(cmd.Parameters[3].Value), "!Convert.IsDBNull(cmd.Parameters[3].Value)");
Debug.Assert(!Convert.IsDBNull(cmd.Parameters[4].Value), "!Convert.IsDBNull(cmd.Parameters[4].Value)");
locked = (bool) cmd.Parameters[2].Value;
lockId = (int) cmd.Parameters[4].Value;
if (locked) {
Debug.Trace("SqlSessionStateStore", "Sql Get returned item that was locked");
Debug.Assert(((int)cmd.Parameters[5].Value & (int)SessionStateActions.InitializeItem) == 0,
"(cmd.Parameters[5].Value & SessionStateActions.InitializeItem) == 0; uninit item shouldn't be locked");
if (useGetLockAge) {
lockAge = new TimeSpan(0, 0, (int) cmd.Parameters[3].Value);
}
else {
DateTime lockDate;
lockDate = (DateTime) cmd.Parameters[3].Value;
lockAge = DateTime.Now - lockDate;
}
Debug.Trace("SqlSessionStateStore", "LockAge = " + lockAge);
if (lockAge > new TimeSpan(0, 0, Sec.ONE_YEAR)) {
Debug.Trace("SqlSessionStateStore", "Lock age is more than 1 year!!!");
lockAge = TimeSpan.Zero;
}
return null;
}
actionFlags = (SessionStateActions) cmd.Parameters[5].Value;
if (buf == null) {
/* Get short item */
Debug.Assert(!Convert.IsDBNull(cmd.Parameters[1].Value), "!Convert.IsDBNull(cmd.Parameters[1].Value)");
Debug.Trace("SqlSessionStateStore", "Sql Get returned short item");
buf = (byte[]) cmd.Parameters[1].Value;
Debug.Assert(buf != null, "buf != null");
}
// Done with the connection.
DisposeOrReuseConnection(ref conn, usePooling);
using(stream = new MemoryStream(buf)) {
item = SessionStateUtility.DeserializeStoreData(context, stream, s_configCompressionEnabled);
_rqOrigStreamLen = (int) stream.Position;
}
return item;
}
finally {
DisposeOrReuseConnection(ref conn, usePooling);
}
}
public override SessionStateStoreData GetItem(HttpContext context,
String id,
out bool locked,
out TimeSpan lockAge,
out object lockId,
out SessionStateActions actionFlags) {
Debug.Trace("SqlSessionStateStore", "Calling Sql Get, id=" + id);
SessionIDManager.CheckIdLength(id, true /* throwOnFail */);
return DoGet(context, id, false, out locked, out lockAge, out lockId, out actionFlags);
}
public override SessionStateStoreData GetItemExclusive(HttpContext context,
String id,
out bool locked,
out TimeSpan lockAge,
out object lockId,
out SessionStateActions actionFlags) {
Debug.Trace("SqlSessionStateStore", "Calling Sql GetExclusive, id=" + id);
SessionIDManager.CheckIdLength(id, true /* throwOnFail */);
return DoGet(context, id, true, out locked, out lockAge, out lockId, out actionFlags);
}
public override void ReleaseItemExclusive(HttpContext context,
String id,
object lockId) {
Debug.Trace("SqlSessionStateStore", "Calling Sql ReleaseExclusive, id=" + id);
Debug.Assert(lockId != null, "lockId != null");
Debug.Assert(context != null, "context != null");
bool usePooling = true;
SqlStateConnection conn = null;
int lockCookie = (int)lockId;
try {
SessionIDManager.CheckIdLength(id, true /* throwOnFail */);
conn = GetConnection(id, ref usePooling);
SqlCommand cmd = conn.TempReleaseExclusive;
cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix;
cmd.Parameters[1].Value = lockCookie;
SqlExecuteNonQueryWithRetry(cmd, false, null);
}
finally {
DisposeOrReuseConnection(ref conn, usePooling);
}
}
public override void SetAndReleaseItemExclusive(HttpContext context,
String id,
SessionStateStoreData item,
object lockId,
bool newItem) {
byte [] buf;
int length;
SqlCommand cmd;
bool usePooling = true;
SqlStateConnection conn = null;
int lockCookie;
Debug.Assert(context != null, "context != null");
try {
Debug.Trace("SqlSessionStateStore", "Calling Sql Set, id=" + id);
Debug.Assert(item.Items != null, "item.Items != null");
Debug.Assert(item.StaticObjects != null, "item.StaticObjects != null");
SessionIDManager.CheckIdLength(id, true /* throwOnFail */);
try {
SessionStateUtility.SerializeStoreData(item, ITEM_SHORT_LENGTH, out buf, out length, s_configCompressionEnabled);
}
catch {
if (!newItem) {
((SessionStateStoreProviderBase)this).ReleaseItemExclusive(context, id, lockId);
}
throw;
}
// Save it to the store
if (lockId == null) {
lockCookie = 0;
}
else {
lockCookie = (int)lockId;
}
conn = GetConnection(id, ref usePooling);
if (!newItem) {
Debug.Assert(_rqOrigStreamLen > 0, "_rqOrigStreamLen > 0");
if (length <= ITEM_SHORT_LENGTH) {
if (_rqOrigStreamLen <= ITEM_SHORT_LENGTH) {
cmd = conn.TempUpdateShort;
}
else {
cmd = conn.TempUpdateShortNullLong;
}
}
else {
if (_rqOrigStreamLen <= ITEM_SHORT_LENGTH) {
cmd = conn.TempUpdateLongNullShort;
}
else {
cmd = conn.TempUpdateLong;
}
}
}
else {
if (length <= ITEM_SHORT_LENGTH) {
cmd = conn.TempInsertShort;
}
else {
cmd = conn.TempInsertLong;
}
}
cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix;
cmd.Parameters[1].Size = length;
cmd.Parameters[1].Value = buf;
cmd.Parameters[2].Value = item.Timeout;
if (!newItem) {
cmd.Parameters[3].Value = lockCookie;
}
SqlExecuteNonQueryWithRetry(cmd, newItem, id);
}
finally {
DisposeOrReuseConnection(ref conn, usePooling);
}
}
public override void RemoveItem(HttpContext context,
String id,
object lockId,
SessionStateStoreData item) {
Debug.Trace("SqlSessionStateStore", "Calling Sql Remove, id=" + id);
Debug.Assert(lockId != null, "lockId != null");
Debug.Assert(context != null, "context != null");
bool usePooling = true;
SqlStateConnection conn = null;
int lockCookie = (int)lockId;
try {
SessionIDManager.CheckIdLength(id, true /* throwOnFail */);
conn = GetConnection(id, ref usePooling);
SqlCommand cmd = conn.TempRemove;
cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix;
cmd.Parameters[1].Value = lockCookie;
SqlExecuteNonQueryWithRetry(cmd, false, null);
}
finally {
DisposeOrReuseConnection(ref conn, usePooling);
}
}
public override void ResetItemTimeout(HttpContext context, String id) {
Debug.Trace("SqlSessionStateStore", "Calling Sql ResetTimeout, id=" + id);
Debug.Assert(context != null, "context != null");
bool usePooling = true;
SqlStateConnection conn = null;
try {
SessionIDManager.CheckIdLength(id, true /* throwOnFail */);
conn = GetConnection(id, ref usePooling);
SqlCommand cmd = conn.TempResetTimeout;
cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix;
SqlExecuteNonQueryWithRetry(cmd, false, null);
}
finally {
DisposeOrReuseConnection(ref conn, usePooling);
}
}
public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
{
Debug.Assert(context != null, "context != null");
return SessionStateUtility.CreateLegitStoreData(context, null, null, timeout);
}
public override void CreateUninitializedItem(HttpContext context, String id, int timeout) {
Debug.Trace("SqlSessionStateStore", "Calling Sql InsertUninitializedItem, id=" + id);
Debug.Assert(context != null, "context != null");
bool usePooling = true;
SqlStateConnection conn = null;
byte [] buf;
int length;
try {
SessionIDManager.CheckIdLength(id, true /* throwOnFail */);
// Store an empty data
SessionStateUtility.SerializeStoreData(CreateNewStoreData(context, timeout),
ITEM_SHORT_LENGTH, out buf, out length, s_configCompressionEnabled);
conn = GetConnection(id, ref usePooling);
SqlCommand cmd = conn.TempInsertUninitializedItem;
cmd.Parameters[0].Value = id + _partitionInfo.AppSuffix;
cmd.Parameters[1].Size = length;
cmd.Parameters[1].Value = buf;
cmd.Parameters[2].Value = timeout;
SqlExecuteNonQueryWithRetry(cmd, true, id);
}
finally {
DisposeOrReuseConnection(ref conn, usePooling);
}
}
static bool IsInsertPKException(SqlException ex, bool ignoreInsertPKException, string id) {
// If the severity is greater than 20, we have a serious error.
// The server usually closes the connection in these cases.
if (ex != null &&
ex.Number == SQL_ERROR_PRIMARY_KEY_VIOLATION &&
ignoreInsertPKException) {
Debug.Trace("SessionStateClientSet",
"Insert failed because of primary key violation; just leave gracefully; id=" + id);
// It's possible that two threads (from the same session) are creating the session
// state, both failed to get it first, and now both tried to insert it.
// One thread may lose with a Primary Key Violation error. If so, that thread will
// just lose and exit gracefully.
return true;
}
return false;
}
static bool IsFatalSqlException(SqlException ex) {
// We will retry sql operations for serious errors.
// We consider fatal exceptions any error with severity >= 20.
// In this case, the SQL server closes the connection.
//
if(ex != null &&
(ex.Class >= 20 ||
ex.Number == SQL_CANNOT_OPEN_DATABASE_FOR_LOGIN ||
ex.Number == SQL_TIMEOUT_EXPIRED)) {
return true;
}
return false;
}
static void ClearFlagForClearPoolInProgress() {
// clear s_isClearPoolInProgress if it was set
Interlocked.CompareExchange(ref s_isClearPoolInProgress, 0, 1);
}
static bool CanRetry(SqlException ex, SqlConnection conn,
ref bool isFirstAttempt, ref DateTime endRetryTime) {
if (s_retryInterval.Seconds <= 0) {
// no retry policy set
return false;
}
if (!IsFatalSqlException(ex)) {
if (!isFirstAttempt) {
ClearFlagForClearPoolInProgress();
}
return false;
}
if (isFirstAttempt) {
// check if someone has called ClearPool for this connection string
// s_isClearPoolInProgress can be:
// 0 - no one called ClearPool;
// 1 - ClearPool is in progress or has already been called
// If no one called ClearPool (s_isClearPoolInProgress = 0), then
// make s_isClearPoolInProgress 1 and call clear pool
if (0 == Interlocked.CompareExchange(ref s_isClearPoolInProgress, 1, 0)) {
Debug.Trace("SqlSessionStateStore", "CanRetry: Call ClearPool to destroy the corrupted connections in the pool");
SqlConnection.ClearPool(conn);
}
// First time we sleep longer than for subsequent retries.
Thread.Sleep(FIRST_RETRY_SLEEP_TIME);
endRetryTime = DateTime.UtcNow.Add(s_retryInterval);
isFirstAttempt = false;
return true;
}
if (DateTime.UtcNow > endRetryTime) {
// the specified retry interval is up, we can't retry anymore
if (!isFirstAttempt) {
ClearFlagForClearPoolInProgress();
}
return false;
}
// sleep the specified time and allow retry
Thread.Sleep(RETRY_SLEEP_TIME);
return true;
}
static int SqlExecuteNonQueryWithRetry(SqlCommand cmd, bool ignoreInsertPKException, string id) {
bool isFirstAttempt = true;
DateTime endRetryTime = DateTime.UtcNow;
while(true) {
try {
if (cmd.Connection.State != ConnectionState.Open) {
// reopen the connection
// (gets closed if a previous operation throwed a SQL exception with severity >= 20)
cmd.Connection.Open();
}
int result = cmd.ExecuteNonQuery();
// the operation succeeded
// If we retried, it's possible ClearPool has been called.
// In this case, we clear the flag that shows ClearPool is in progress.
if (!isFirstAttempt) {
ClearFlagForClearPoolInProgress();
}
return result;
}
catch (SqlException e) {
// if specified, ignore primary key violations
if (IsInsertPKException(e, ignoreInsertPKException, id)) {
// ignoreInsertPKException = insert && newItem
return -1;
}
if (!CanRetry(e, cmd.Connection, ref isFirstAttempt, ref endRetryTime)) {
// just throw, because not all conditions to retry are satisfied
ThrowSqlConnectionException(cmd.Connection, e);
}
}
catch (Exception e) {
// just throw, we have a different Exception
ThrowSqlConnectionException(cmd.Connection, e);
}
}
}
static SqlDataReader SqlExecuteReaderWithRetry(SqlCommand cmd, CommandBehavior cmdBehavior) {
bool isFirstAttempt = true;
DateTime endRetryTime = DateTime.UtcNow;
while(true) {
try {
if (cmd.Connection.State != ConnectionState.Open) {
// reopen the connection
// (gets closed if a previous operation throwed a SQL exception with severity >= 20)
cmd.Connection.Open();
}
SqlDataReader reader = cmd.ExecuteReader(cmdBehavior);
// the operation succeeded
if (!isFirstAttempt) {
ClearFlagForClearPoolInProgress();
}
return reader;
}
catch (SqlException e) {
if (!CanRetry(e, cmd.Connection, ref isFirstAttempt, ref endRetryTime)) {
// just throw, default to previous behavior
ThrowSqlConnectionException(cmd.Connection, e);
}
}
catch (Exception e) {
// just throw, we have a different Exception
ThrowSqlConnectionException(cmd.Connection, e);
}
}
}
internal class SqlPartitionInfo : PartitionInfo {
bool _useIntegratedSecurity;
string _sqlConnectionString;
string _tracingPartitionString;
SupportFlags _support = SupportFlags.Uninitialized;
string _appSuffix;
object _lock = new object();
bool _sqlInfoInited;
const string APP_SUFFIX_FORMAT = "x8";
const int APPID_MAX = 280;
const int SQL_2000_MAJ_VER = 8;
internal SqlPartitionInfo(ResourcePool rpool, bool useIntegratedSecurity, string sqlConnectionString)
: base(rpool) {
_useIntegratedSecurity = useIntegratedSecurity;
_sqlConnectionString = sqlConnectionString;
Debug.Trace("PartitionInfo", "Created a new info, sqlConnectionString=" + sqlConnectionString);
}
internal bool UseIntegratedSecurity {
get { return _useIntegratedSecurity; }
}
internal string SqlConnectionString {
get { return _sqlConnectionString; }
}
internal SupportFlags SupportFlags {
get { return _support; }
set { _support = value; }
}
protected override string TracingPartitionString {
get {
if (_tracingPartitionString == null) {
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(_sqlConnectionString);
builder.Password = String.Empty;
builder.UserID = String.Empty;
_tracingPartitionString = builder.ConnectionString;
}
return _tracingPartitionString;
}
}
internal string AppSuffix {
get { return _appSuffix; }
}
void GetServerSupportOptions(SqlConnection sqlConnection) {
Debug.Assert(SupportFlags == SupportFlags.Uninitialized);
SqlCommand cmd;
SqlDataReader reader = null;
SupportFlags flags = SupportFlags.None;
bool v2 = false;
SqlParameter p;
// First, check if the SQL server is running Whidbey scripts
cmd = new SqlCommand("Select name from sysobjects where type = 'P' and name = 'TempGetVersion'", sqlConnection);
cmd.CommandType = CommandType.Text;
using(reader = SqlExecuteReaderWithRetry(cmd, CommandBehavior.SingleRow)) {
if (reader.Read()) {
// This function first appears in Whidbey (v2). So we know it's
// at least 2.0 even without reading its content.
v2 = true;
}
}
if (!v2) {
if (s_usePartition) {
throw new HttpException(
SR.GetString(SR.Need_v2_SQL_Server_partition_resolver,
s_configPartitionResolverType, sqlConnection.DataSource, sqlConnection.Database));
}
else {
throw new HttpException(
SR.GetString(SR.Need_v2_SQL_Server));
}
}
// Then, see if it's SQL 2000 or above
cmd = new SqlCommand("dbo.GetMajorVersion", sqlConnection);
cmd.CommandType = CommandType.StoredProcedure;
p = cmd.Parameters.Add(new SqlParameter("@@ver", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
SqlExecuteNonQueryWithRetry(cmd, false, null);
try {
if ((int)p.Value >= SQL_2000_MAJ_VER) {
// For details, see the extensive doc in DoGet method.
flags |= SupportFlags.GetLockAge;
}
Debug.Trace("PartitionInfo", "SupportFlags initialized to " + flags);
SupportFlags = flags;
}
catch (Exception e) {
SqlSessionStateStore.ThrowSqlConnectionException(sqlConnection, e);
}
}
internal void InitSqlInfo(SqlConnection sqlConnection) {
if (_sqlInfoInited) {
return;
}
lock (_lock) {
if (_sqlInfoInited) {
return;
}
GetServerSupportOptions(sqlConnection);
// Get AppSuffix info
SqlParameter p;
SqlCommand cmdTempGetAppId = new SqlCommand("dbo.TempGetAppID", sqlConnection);
cmdTempGetAppId.CommandType = CommandType.StoredProcedure;
cmdTempGetAppId.CommandTimeout = s_commandTimeout;
// AppDomainAppId will contain the whole metabase path of the request's app
// e.g. /lm/w3svc/1/root/fxtest
p = cmdTempGetAppId.Parameters.Add(new SqlParameter("@appName", SqlDbType.VarChar, APPID_MAX));
p.Value = HttpRuntime.AppDomainAppId;
p = cmdTempGetAppId.Parameters.Add(new SqlParameter("@appId", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
p.Value = Convert.DBNull;
cmdTempGetAppId.ExecuteNonQuery();
Debug.Assert(!Convert.IsDBNull(p), "!Convert.IsDBNull(p)");
int appId = (int) p.Value;
_appSuffix = (appId).ToString(APP_SUFFIX_FORMAT, CultureInfo.InvariantCulture);
_sqlInfoInited = true;
}
}
};
/*
Here are all the sprocs created for session state and how they're used:
CreateTempTables
- Called during setup
DeleteExpiredSessions
- Called by SQL agent to remove expired sessions
GetHashCode
- Called by sproc TempGetAppID
GetMajorVersion
- Called during setup
TempGetAppID
- Called when an asp.net application starts up
TempGetStateItem
- Used for ReadOnly session state
- Called by v1 asp.net
- Called by v1.1 asp.net against SQL 7
TempGetStateItem2
- Used for ReadOnly session state
- Called by v1.1 asp.net against SQL 2000
TempGetStateItem3
- Used for ReadOnly session state
- Called by v2 asp.net
TempGetStateItemExclusive
- Called by v1 asp.net
- Called by v1.1 asp.net against SQL 7
TempGetStateItemExclusive2
- Called by v1.1 asp.net against SQL 2000
TempGetStateItemExclusive3
- Called by v2 asp.net
TempGetVersion
- Called by v2 asp.net when an application starts up
TempInsertStateItemLong
- Used when creating a new session state with size > 7000 bytes
TempInsertStateItemShort
- Used when creating a new session state with size <= 7000 bytes
TempInsertUninitializedItem
- Used when creating a new uninitilized session state (cookieless="true" and regenerateExpiredSessionId="true" in config)
TempReleaseStateItemExclusive
- Used when a request that has acquired the session state (exclusively) hit an error during the page execution
TempRemoveStateItem
- Used when a session is abandoned
TempResetTimeout
- Used when a request (with an active session state) is handled by an HttpHandler which doesn't support IRequiresSessionState interface.
TempUpdateStateItemLong
- Used when updating a session state with size > 7000 bytes
TempUpdateStateItemLongNullShort
- Used when updating a session state where original size <= 7000 bytes but new size > 7000 bytes
TempUpdateStateItemShort
- Used when updating a session state with size <= 7000 bytes
TempUpdateStateItemShortNullLong
- Used when updating a session state where original size > 7000 bytes but new size <= 7000 bytes
*/
class SqlStateConnection : IDisposable {
SqlConnection _sqlConnection;
SqlCommand _cmdTempGet;
SqlCommand _cmdTempGetExclusive;
SqlCommand _cmdTempReleaseExclusive;
SqlCommand _cmdTempInsertShort;
SqlCommand _cmdTempInsertLong;
SqlCommand _cmdTempUpdateShort;
SqlCommand _cmdTempUpdateShortNullLong;
SqlCommand _cmdTempUpdateLong;
SqlCommand _cmdTempUpdateLongNullShort;
SqlCommand _cmdTempRemove;
SqlCommand _cmdTempResetTimeout;
SqlCommand _cmdTempInsertUninitializedItem;
SqlPartitionInfo _partitionInfo;
internal SqlStateConnection(SqlPartitionInfo sqlPartitionInfo, TimeSpan retryInterval) {
Debug.Trace("SessionStateConnectionIdentity", "Connecting under " + WindowsIdentity.GetCurrent().Name);
_partitionInfo = sqlPartitionInfo;
_sqlConnection = new SqlConnection(sqlPartitionInfo.SqlConnectionString);
bool isFirstAttempt = true;
DateTime endRetryTime = DateTime.UtcNow;
while(true) {
try {
_sqlConnection.Open();
// the operation succeeded, exit the loop
if(!isFirstAttempt) {
ClearFlagForClearPoolInProgress();
}
break;
}
catch (SqlException e) {
if (e != null &&
(e.Number == SQL_LOGIN_FAILED ||
e.Number == SQL_LOGIN_FAILED_2 ||
e.Number == SQL_LOGIN_FAILED_3))
{
string user;
SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(sqlPartitionInfo.SqlConnectionString);
if (scsb.IntegratedSecurity) {
user = WindowsIdentity.GetCurrent().Name;
}
else {
user = scsb.UserID;
}
HttpException outerException = new HttpException(
SR.GetString(SR.Login_failed_sql_session_database, user ), e);
outerException.SetFormatter(new UseLastUnhandledErrorFormatter(outerException));
ClearConnectionAndThrow(outerException);
}
if (!CanRetry(e, _sqlConnection, ref isFirstAttempt, ref endRetryTime))
{
// just throw, the retry conditions are not satisfied
ClearConnectionAndThrow(e);
}
}
catch (Exception e) {
// just throw, we have a different Exception
ClearConnectionAndThrow(e);
}
}
try {
_partitionInfo.InitSqlInfo(_sqlConnection);
Debug.Assert(sqlPartitionInfo.SupportFlags != SupportFlags.Uninitialized);
PerfCounters.IncrementCounter(AppPerfCounter.SESSION_SQL_SERVER_CONNECTIONS);
}
catch {
Dispose();
throw;
}
}
void ClearConnectionAndThrow(Exception e) {
SqlConnection connection = _sqlConnection;
_sqlConnection = null;
ThrowSqlConnectionException(connection, e);
}
internal void ClearAllParameters() {
ClearAllParameters(_cmdTempGet);
ClearAllParameters(_cmdTempGetExclusive);
ClearAllParameters(_cmdTempReleaseExclusive);
ClearAllParameters(_cmdTempInsertShort);
ClearAllParameters(_cmdTempInsertLong);
ClearAllParameters(_cmdTempUpdateShort);
ClearAllParameters(_cmdTempUpdateShortNullLong);
ClearAllParameters(_cmdTempUpdateLong);
ClearAllParameters(_cmdTempUpdateLongNullShort);
ClearAllParameters(_cmdTempRemove);
ClearAllParameters(_cmdTempResetTimeout);
ClearAllParameters(_cmdTempInsertUninitializedItem);
}
internal void ClearAllParameters(SqlCommand cmd) {
if (cmd == null) {
return;
}
foreach (SqlParameter param in cmd.Parameters) {
param.Value = Convert.DBNull;
}
}
internal SqlCommand TempGet {
get {
if (_cmdTempGet == null) {
SqlParameter p;
_cmdTempGet = new SqlCommand("dbo.TempGetStateItem3", _sqlConnection);
_cmdTempGet.CommandType = CommandType.StoredProcedure;
_cmdTempGet.CommandTimeout = s_commandTimeout;
// Use a different set of parameters for the sprocs that support GetLockAge
if ((_partitionInfo.SupportFlags & SupportFlags.GetLockAge) != 0) {
_cmdTempGet.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
p = _cmdTempGet.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH));
p.Direction = ParameterDirection.Output;
p = _cmdTempGet.Parameters.Add(new SqlParameter("@locked", SqlDbType.Bit));
p.Direction = ParameterDirection.Output;
p = _cmdTempGet.Parameters.Add(new SqlParameter("@lockAge", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
p = _cmdTempGet.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
p = _cmdTempGet.Parameters.Add(new SqlParameter("@actionFlags", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
}
else {
_cmdTempGet.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
p = _cmdTempGet.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH));
p.Direction = ParameterDirection.Output;
p = _cmdTempGet.Parameters.Add(new SqlParameter("@locked", SqlDbType.Bit));
p.Direction = ParameterDirection.Output;
p = _cmdTempGet.Parameters.Add(new SqlParameter("@lockDate", SqlDbType.DateTime));
p.Direction = ParameterDirection.Output;
p = _cmdTempGet.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
p = _cmdTempGet.Parameters.Add(new SqlParameter("@actionFlags", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
}
}
return _cmdTempGet;
}
}
internal SqlCommand TempGetExclusive {
get {
if (_cmdTempGetExclusive == null) {
SqlParameter p;
_cmdTempGetExclusive = new SqlCommand("dbo.TempGetStateItemExclusive3", _sqlConnection);
_cmdTempGetExclusive.CommandType = CommandType.StoredProcedure;
_cmdTempGetExclusive.CommandTimeout = s_commandTimeout;
// Use a different set of parameters for the sprocs that support GetLockAge
if ((_partitionInfo.SupportFlags & SupportFlags.GetLockAge) != 0) {
_cmdTempGetExclusive.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH));
p.Direction = ParameterDirection.Output;
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@locked", SqlDbType.Bit));
p.Direction = ParameterDirection.Output;
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@lockAge", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@actionFlags", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
}
else {
_cmdTempGetExclusive.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH));
p.Direction = ParameterDirection.Output;
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@locked", SqlDbType.Bit));
p.Direction = ParameterDirection.Output;
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@lockDate", SqlDbType.DateTime));
p.Direction = ParameterDirection.Output;
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
p = _cmdTempGetExclusive.Parameters.Add(new SqlParameter("@actionFlags", SqlDbType.Int));
p.Direction = ParameterDirection.Output;
}
}
return _cmdTempGetExclusive;
}
}
internal SqlCommand TempReleaseExclusive {
get {
if (_cmdTempReleaseExclusive == null) {
/* ReleaseExlusive */
_cmdTempReleaseExclusive = new SqlCommand("dbo.TempReleaseStateItemExclusive", _sqlConnection);
_cmdTempReleaseExclusive.CommandType = CommandType.StoredProcedure;
_cmdTempReleaseExclusive.CommandTimeout = s_commandTimeout;
_cmdTempReleaseExclusive.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
_cmdTempReleaseExclusive.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
}
return _cmdTempReleaseExclusive;
}
}
internal SqlCommand TempInsertLong {
get {
if (_cmdTempInsertLong == null) {
_cmdTempInsertLong = new SqlCommand("dbo.TempInsertStateItemLong", _sqlConnection);
_cmdTempInsertLong.CommandType = CommandType.StoredProcedure;
_cmdTempInsertLong.CommandTimeout = s_commandTimeout;
_cmdTempInsertLong.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
_cmdTempInsertLong.Parameters.Add(new SqlParameter("@itemLong", SqlDbType.Image, 8000));
_cmdTempInsertLong.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int));
}
return _cmdTempInsertLong;
}
}
internal SqlCommand TempInsertShort {
get {
/* Insert */
if (_cmdTempInsertShort == null) {
_cmdTempInsertShort = new SqlCommand("dbo.TempInsertStateItemShort", _sqlConnection);
_cmdTempInsertShort.CommandType = CommandType.StoredProcedure;
_cmdTempInsertShort.CommandTimeout = s_commandTimeout;
_cmdTempInsertShort.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
_cmdTempInsertShort.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH));
_cmdTempInsertShort.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int));
}
return _cmdTempInsertShort;
}
}
internal SqlCommand TempUpdateLong {
get {
if (_cmdTempUpdateLong == null) {
_cmdTempUpdateLong = new SqlCommand("dbo.TempUpdateStateItemLong", _sqlConnection);
_cmdTempUpdateLong.CommandType = CommandType.StoredProcedure;
_cmdTempUpdateLong.CommandTimeout = s_commandTimeout;
_cmdTempUpdateLong.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
_cmdTempUpdateLong.Parameters.Add(new SqlParameter("@itemLong", SqlDbType.Image, 8000));
_cmdTempUpdateLong.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int));
_cmdTempUpdateLong.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
}
return _cmdTempUpdateLong;
}
}
internal SqlCommand TempUpdateShort {
get {
/* Update */
if (_cmdTempUpdateShort == null) {
_cmdTempUpdateShort = new SqlCommand("dbo.TempUpdateStateItemShort", _sqlConnection);
_cmdTempUpdateShort.CommandType = CommandType.StoredProcedure;
_cmdTempUpdateShort.CommandTimeout = s_commandTimeout;
_cmdTempUpdateShort.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
_cmdTempUpdateShort.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH));
_cmdTempUpdateShort.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int));
_cmdTempUpdateShort.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
}
return _cmdTempUpdateShort;
}
}
internal SqlCommand TempUpdateShortNullLong {
get {
if (_cmdTempUpdateShortNullLong == null) {
_cmdTempUpdateShortNullLong = new SqlCommand("dbo.TempUpdateStateItemShortNullLong", _sqlConnection);
_cmdTempUpdateShortNullLong.CommandType = CommandType.StoredProcedure;
_cmdTempUpdateShortNullLong.CommandTimeout = s_commandTimeout;
_cmdTempUpdateShortNullLong.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
_cmdTempUpdateShortNullLong.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH));
_cmdTempUpdateShortNullLong.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int));
_cmdTempUpdateShortNullLong.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
}
return _cmdTempUpdateShortNullLong;
}
}
internal SqlCommand TempUpdateLongNullShort {
get {
if (_cmdTempUpdateLongNullShort == null) {
_cmdTempUpdateLongNullShort = new SqlCommand("dbo.TempUpdateStateItemLongNullShort", _sqlConnection);
_cmdTempUpdateLongNullShort.CommandType = CommandType.StoredProcedure;
_cmdTempUpdateLongNullShort.CommandTimeout = s_commandTimeout;
_cmdTempUpdateLongNullShort.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
_cmdTempUpdateLongNullShort.Parameters.Add(new SqlParameter("@itemLong", SqlDbType.Image, 8000));
_cmdTempUpdateLongNullShort.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int));
_cmdTempUpdateLongNullShort.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
}
return _cmdTempUpdateLongNullShort;
}
}
internal SqlCommand TempRemove {
get {
if (_cmdTempRemove == null) {
/* Remove */
_cmdTempRemove = new SqlCommand("dbo.TempRemoveStateItem", _sqlConnection);
_cmdTempRemove.CommandType = CommandType.StoredProcedure;
_cmdTempRemove.CommandTimeout = s_commandTimeout;
_cmdTempRemove.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
_cmdTempRemove.Parameters.Add(new SqlParameter("@lockCookie", SqlDbType.Int));
}
return _cmdTempRemove;
}
}
internal SqlCommand TempInsertUninitializedItem {
get {
if (_cmdTempInsertUninitializedItem == null) {
_cmdTempInsertUninitializedItem = new SqlCommand("dbo.TempInsertUninitializedItem", _sqlConnection);
_cmdTempInsertUninitializedItem.CommandType = CommandType.StoredProcedure;
_cmdTempInsertUninitializedItem.CommandTimeout = s_commandTimeout;
_cmdTempInsertUninitializedItem.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
_cmdTempInsertUninitializedItem.Parameters.Add(new SqlParameter("@itemShort", SqlDbType.VarBinary, ITEM_SHORT_LENGTH));
_cmdTempInsertUninitializedItem.Parameters.Add(new SqlParameter("@timeout", SqlDbType.Int));
}
return _cmdTempInsertUninitializedItem;
}
}
internal SqlCommand TempResetTimeout {
get {
if (_cmdTempResetTimeout == null) {
/* ResetTimeout */
_cmdTempResetTimeout = new SqlCommand("dbo.TempResetTimeout", _sqlConnection);
_cmdTempResetTimeout.CommandType = CommandType.StoredProcedure;
_cmdTempResetTimeout.CommandTimeout = s_commandTimeout;
_cmdTempResetTimeout.Parameters.Add(new SqlParameter("@id", SqlDbType.NVarChar, ID_LENGTH));
}
return _cmdTempResetTimeout;
}
}
public void Dispose() {
Debug.Trace("ResourcePool", "Disposing SqlStateConnection");
if (_sqlConnection != null) {
_sqlConnection.Close();
_sqlConnection = null;
PerfCounters.DecrementCounter(AppPerfCounter.SESSION_SQL_SERVER_CONNECTIONS);
}
}
internal SqlConnection Connection {
get { return _sqlConnection; }
}
}
}
}
|