Files
DP44/Common/DTS.Common.Storage/LockManager.cs
2026-04-17 14:55:32 -04:00

272 lines
14 KiB
C#

using DTS.Common.Classes.Locking;
using DTS.Common.Utilities.Logging;
using System;
using System.Data;
using System.Data.SqlClient;
namespace DTS.Common.Storage
{
public class LockManager
{
public static bool FreeLock(int itemId, string userName, int? userId, out LockError error, int categoryId)
{
error = null;
try
{
using (var cmd = DbOperations.GetSQLCommand(true))
{
try
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = DbOperationsEnum.StoredProcedure.sp_LockFree.ToString();
#region params
cmd.Parameters.Add(new SqlParameter("@UserId", SqlDbType.Int) { Value = userId });
cmd.Parameters.Add(new SqlParameter("@UserName", SqlDbType.NVarChar, 50) { Value = userName });
cmd.Parameters.Add(new SqlParameter("@ItemId", SqlDbType.Int) { Value = itemId });
cmd.Parameters.Add(new SqlParameter("@CategoryId", SqlDbType.Int) { Value = categoryId });
var errorNumber = new SqlParameter("@errorNumber", SqlDbType.Int) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(errorNumber);
var errorMessage = new SqlParameter("@errorMessage", SqlDbType.NVarChar, 250) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(errorMessage);
#endregion params
cmd.ExecuteNonQuery();
var errorCode = int.Parse(errorNumber.Value.ToString());
switch (errorCode)
{
case 0: return true; //all cool, lock updated
case 1: //test setup not found
case 2: //user not found
error = new LockError(errorCode,
(errorMessage.Value as string) ?? $"{errorCode}");
return false;
case 3: //no lock to update
error = new LockError(3, (errorMessage.Value as string) ?? $"{errorCode}");
return false;
default:
error = new LockError(4, (errorMessage.Value as string) ?? $"{errorCode}");
return false;
}
}
finally { cmd.Connection.Dispose(); }
}
}
catch (Exception ex)
{
APILogger.Log(ex);
return false;
}
}
public static bool UpdateLock(int itemId, int categoryId, string userName, int? userId, out LockError error)
{
error = null;
try
{
using (var cmd = DbOperations.GetSQLCommand(true))
{
try
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = DbOperationsEnum.StoredProcedure.sp_LockUpdate.ToString();
#region params
cmd.Parameters.Add(new SqlParameter("@UserId", SqlDbType.Int) { Value = userId });
cmd.Parameters.Add(new SqlParameter("@UserName", SqlDbType.NVarChar, 50) { Value = userName });
cmd.Parameters.Add(new SqlParameter("@ItemId", SqlDbType.Int) { Value = itemId });
cmd.Parameters.Add(new SqlParameter("@CategoryId", SqlDbType.Int) { Value = categoryId });
cmd.Parameters.Add(new SqlParameter("@MachineName", SqlDbType.NVarChar, 50) { Value = Environment.MachineName });
var errorNumber = new SqlParameter("@errorNumber", SqlDbType.Int) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(errorNumber);
var errorMessage = new SqlParameter("@errorMessage", SqlDbType.NVarChar, 250) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(errorMessage);
var lockingUser = new SqlParameter("@LockingUser", SqlDbType.NVarChar, 50) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lockingUser);
var lockingMachine = new SqlParameter("@LockingMachine", SqlDbType.NVarChar, 50) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lockingMachine);
var lastTimeUsed = new SqlParameter("@LastUsedTime", SqlDbType.DateTime) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lastTimeUsed);
var lockCreateTime = new SqlParameter("@LockCreateTime", SqlDbType.DateTime) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lockCreateTime);
#endregion params
cmd.ExecuteNonQuery();
var errorCode = int.Parse(errorNumber.Value.ToString());
switch (errorCode)
{
case 0: return true; //all cool, lock updated
case 1: //test setup not found
case 2: //user not found
error = new LockError(errorCode,
(errorMessage.Value as string) ?? $"{errorCode}");
return false;
case 3: //no lock to update
error = new LockError(LockError.LOCKDOESNTEXIST_ERROR, (errorMessage.Value as string) ?? $"{errorCode}");
return false;
case 4: //lock stolen
error = new LockError(LockError.LOCKSTOLEN_ERROR,
$"Lock now belongs to user {lockingUser.Value}:{lockingMachine.Value}", (string)lockingUser.Value, (string)lockingMachine.Value);
return false;
default:
error = new LockError(4, (errorMessage.Value as string) ?? $"{errorCode}");
return false;
}
}
finally { cmd.Connection.Dispose(); }
}
}
catch (SqlException ex)
{
switch (ex.Number)
{
case ERROR_BAD_NETPATH:
error = new LockError(LockError.BAD_NETPATH_ERROR, Strings.Strings.BAD_NETPATH_ERROR_MSG);
break;
case ERROR_SEM_TIMEOUT:
error = new LockError(LockError.SEM_TIMEOUT_ERROR, Strings.Strings.SEM_TIME_ERROR_MSG);
break;
}
APILogger.Log("SqlException in UpdateLock", ex);
return false;
}
catch (Exception ex)
{
APILogger.Log("Exception in UpdateLock", ex);
return false;
}
}
/// <summary>
/// The semaphore timeout period has expired
/// https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-?redirectedfrom=MSDN
/// </summary>
private const int ERROR_SEM_TIMEOUT = 0x00000079;
/// <summary>
/// network path not found, apparently also can occur during lock management?
/// 14782 Improve lost remote db connection modal dialogs
/// </summary>
private const int ERROR_BAD_NETPATH = 0x00000035;
public enum ItemCategories
{
TestSetup,
Group,
Sensor,
SystemSettings
}
public static bool LockItem(string itemKey,
int itemId,
ItemCategories itemCategory,
string userName,
int? userId,
out LockRecord existingLock, out
LockError error)
{
error = null;
existingLock = null;
try
{
using (var cmd = DbOperations.GetSQLCommand(true))
{
try
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = DbOperationsEnum.StoredProcedure.sp_LockGet.ToString();
#region params
int? id = null;
if (itemId > 0) { id = itemId; } //NOTE YOU"LL NEED THE ID TO FREE, AND THE CURRENT SP DOESN'T RETURN IT
cmd.Parameters.Add(new SqlParameter("@UserId", SqlDbType.Int) { Value = userId });
cmd.Parameters.Add(new SqlParameter("@UserName", SqlDbType.NVarChar, 50) { Value = userName });
cmd.Parameters.Add(new SqlParameter("@ItemId", SqlDbType.Int) { Value = id });
cmd.Parameters.Add(new SqlParameter("@ItemKey", SqlDbType.NVarChar, 50) { Value = itemKey });
cmd.Parameters.Add(new SqlParameter("@ItemCategory", SqlDbType.NVarChar, 50) { Value = itemCategory.ToString() });
cmd.Parameters.Add(new SqlParameter("@MachineName", SqlDbType.NVarChar, 50) { Value = Environment.MachineName });
var errorNumber = new SqlParameter("@errorNumber", SqlDbType.Int) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(errorNumber);
var errorMessage = new SqlParameter("@errorMessage", SqlDbType.NVarChar, 250) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(errorMessage);
var lockingUser = new SqlParameter("@LockingUser", SqlDbType.NVarChar, 50) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lockingUser);
var lockingMachine = new SqlParameter("@LockingMachineName", SqlDbType.NVarChar, 50) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lockingMachine);
var lastTimeUsed = new SqlParameter("@LastUsedTime", SqlDbType.DateTime) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lastTimeUsed);
var lockCreateTime = new SqlParameter("@LockCreateTime", SqlDbType.DateTime) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lockCreateTime);
var lockItemCategory = new SqlParameter("@LockedItemCategory", SqlDbType.Int) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lockItemCategory);
var lockedItemId = new SqlParameter("@LockedItemId", SqlDbType.Int) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(lockedItemId);
#endregion params
cmd.ExecuteNonQuery();
int resultItemId = itemId;
if (itemId < 0)
{
if (!DBNull.Value.Equals(lockedItemId.Value))
{
resultItemId = Convert.ToInt32(lockedItemId.Value);
}
}
var errorCode = int.Parse(errorNumber.Value.ToString());
switch (errorCode)
{
case 0:
existingLock = new LockRecord(userName, Environment.MachineName, DateTime.Now, DateTime.Now, itemKey, Convert.ToInt32(lockItemCategory.Value), resultItemId);
return true; //all cool, locked away
case 1: //item not found
error = new LockError(LockError.ITEM_NOT_FOUND, (errorMessage.Value as string) ?? $"{errorCode}");
return false;
case 2: //user not found
error = new LockError(errorCode,
(errorMessage.Value as string) ?? $"{errorCode}");
return false;
case 3: //already locked
existingLock = new LockRecord((string)lockingUser.Value,
(string)lockingMachine.Value, Convert.ToDateTime(lockCreateTime.Value),
Convert.ToDateTime(lastTimeUsed.Value), itemKey, Convert.ToInt32(lockItemCategory.Value), resultItemId);
error = new LockError(errorCode, (errorMessage.Value as string) ?? $"{errorCode}");
return false;
default:
error = new LockError(LockError.UNKNOWN_ERROR, (errorMessage.Value as string) ?? $"{errorCode}");
return false;
}
}
finally { cmd.Connection.Dispose(); }
}
}
catch (Exception ex)
{
APILogger.Log(ex);
error = new LockError(LockError.UNKNOWN_ERROR, ex.Message);
return false;
}
}
}
}