EnVisageOnline/Main/Source/EnVisage/App_Start/ContentLocker.cs

355 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using EnVisage.Code;
using EnVisage.Code.BLL;
using EnVisage.Code.Cache;
using EnVisage.Models;
using EnVisage.Properties;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using System.Web;
namespace EnVisage.App_Start
{
public class LockerResponse
{
public enum ResponseCode
{
OK = 0,
ObjectLocked = 1,
ObjectExpired = 2,
}
public ResponseCode Code { get; set; }
public bool Status { get; set; }
public string LockedBy { get; set; }
public string EntityTitle { get; set; }
public LockerResponse()
{
Code = ResponseCode.OK;
}
}
public static class ContentLocker
{
static readonly Dictionary<string, string> EntityTitles = new Dictionary<string, string>
{
{"SystemSettings", "System Settings"},
{"Clients", "Client"},
{"CreditDepartment", "Credit Department"},
{"ExpenditureCategory", "Expenditure Category"},
{"GLAccount", "GL Account"},
{"NonProjectTime", "Non-Project Time"},
{"ScenarioGroup", "Scenario Group"},
{"ExpenditureCategoryRate", "Rate"},
{"Project Statuses", "Project Status"},
{"Types", "Type"},
{"UnitOfMeasure", "Unit Of Measure"},
{"NonProjectTimeCategories", "Non-Project Time Categories"},
{"PeopleResource", "People Resource"}
};
static Timer timer;
static Dictionary<string, Dictionary<string, LockedElement>> lockersList;
static ContentLocker()
{
lockersList = new Dictionary<string, Dictionary<string, LockedElement>>();
timer = new Timer(Settings.Default.CheckInterval);
timer.Elapsed += СheckDeadLocks;
if (!timer.Enabled)
timer.Start();
}
static void СheckDeadLocks(object sender, ElapsedEventArgs e)
{
foreach (var lockedElement in lockersList)
{
UnlockDead(lockedElement.Value);
}
}
static void UnlockDead(Dictionary<string, LockedElement> lockedList)
{
List<string> unlockItems = new List<string>();
foreach (var lockedElement in lockedList)
{
if (lockedElement.Value.GetReleaseDateTime() < DateTime.Now)
{
unlockItems.Add(lockedElement.Key);
}
}
foreach (var unlockId in unlockItems)
{
lockedList.Remove(unlockId);
}
}
/// <summary>
/// Adds an object lock to the dictionary (if possible) and returns result of this operation.
/// </summary>
/// <param name="tableId">Unique identifier of the database table in which we store the locked object.</param>
/// <param name="fieldId">Unique identifier of the locked object.</param>
/// <param name="owner">Username of the owner of the lock.</param>
/// <param name="timestamp">A timestamp of the object to check that system works with actual data.</param>
/// <returns>An instance of <see cref="LockerResponse"/> with info about operation result.</returns>
public static LockerResponse AddLock(String tableId, String fieldId, string owner, string timestamp)
{
// add a table to the main dictionary if there is no such table yet
if (!lockersList.ContainsKey(tableId))
{
lockersList.Add(tableId, new Dictionary<string, LockedElement>());
}
// if there is a lock on the specified object but made by another user then return failed result with info about locked object
if (lockersList[tableId].ContainsKey(fieldId) && lockersList[tableId][fieldId].Owner != owner)
{
return new LockerResponse { Status = false, Code = LockerResponse.ResponseCode.ObjectLocked, LockedBy = lockersList[tableId][fieldId].Owner, EntityTitle = GetLockedEntityTitle(tableId) };
}
// if there is a lock on the specified object made by current user then do not add lock and return successfull result
if (lockersList[tableId].ContainsKey(fieldId) && lockersList[tableId][fieldId].Owner == owner)
{
return new LockerResponse { Status = true, EntityTitle = GetLockedEntityTitle(tableId) };
}
// if there is no lock on the specified object but it has been updated since last access then return failed result with info about modified object
if (!IsObjectValid(tableId, fieldId, timestamp))
{
return new LockerResponse { Status = false, Code = LockerResponse.ResponseCode.ObjectExpired, EntityTitle = GetLockedEntityTitle(tableId) };
}
// add a lock to the specified object by current user
lockersList[tableId].Add(fieldId, new LockedElement(owner));
// return successfull result
return new LockerResponse { Status = true, EntityTitle = GetLockedEntityTitle(tableId) };
}
public static bool RemoveLock(String tableId, String fieldId, string owner)
{
if (!lockersList.ContainsKey(tableId))
{
return false;
}
if (!lockersList[tableId].ContainsKey(fieldId))
{
return false;
}
if (lockersList[tableId][fieldId].Owner == owner)
{
lockersList[tableId].Remove(fieldId);
return true;
}
return false;
}
/// <summary>
/// Removed all locks for specified fieldId
/// </summary>
/// <param name="fieldId"></param>
/// <returns>Locks removed count</returns>
public static int RemoveAllLocks(String fieldId)
{
if (String.IsNullOrWhiteSpace(fieldId))
return 0;
var fieldIdProcessed = fieldId.ToLower().Trim();
int removedLocksCount = 0;
var lockedTableKeys = lockersList.Keys.ToList();
for (var tIndex = lockedTableKeys.Count - 1; tIndex >= 0; tIndex--)
{
var tableKey = lockedTableKeys[tIndex];
var tableLocks = lockersList[tableKey];
if ((tableLocks != null) && (tableLocks.Keys.Count > 0))
{
var fieldKeys = tableLocks.Keys.ToList();
for (var fIndex = fieldKeys.Count - 1; fIndex >= 0; fIndex--)
{
var currentFieldKey = fieldKeys[fIndex];
if (currentFieldKey.ToLower().Trim() == fieldIdProcessed)
{
RemoveLock(tableKey, currentFieldKey, tableLocks[currentFieldKey].Owner);
removedLocksCount++;
}
}
}
}
return removedLocksCount;
}
public static bool UpdateLock(String tableId, String fieldId, string owner)
{
if (!lockersList.ContainsKey(tableId))
{
return false;
}
if (!lockersList[tableId].ContainsKey(fieldId))
{
return false;
}
if (lockersList[tableId][fieldId].Owner == owner)
{
lockersList[tableId][fieldId].UpdateLock();
return true;
}
return false;
}
public static string[] getLockList(String tableId)
{
if (lockersList.ContainsKey(tableId))
{
List<string> lockItems = new List<string>();
foreach (var lockedElement in lockersList)
{
lockItems.Add(lockedElement.Key);
}
return lockItems.ToArray();
}
return null;
}
public static LockerResponse IsLocked(String tableId, String fieldId, string owner)
{
if (lockersList.ContainsKey(tableId) && lockersList[tableId].ContainsKey(fieldId) && lockersList[tableId][fieldId].Owner != owner)
{
// if lock is expired then we should release it
if (lockersList[tableId][fieldId].GetReleaseDateTime() < DateTime.Now)
{
lockersList[tableId].Remove(fieldId);
return new LockerResponse { Status = false, EntityTitle = GetLockedEntityTitle(tableId) };
}
// object is locked by another user
return new LockerResponse { Status = true, Code = LockerResponse.ResponseCode.ObjectLocked, LockedBy = lockersList[tableId][fieldId].Owner, EntityTitle = GetLockedEntityTitle(tableId) };
}
// object is free to edit
return new LockerResponse { Status = false, EntityTitle = GetLockedEntityTitle(tableId) };
}
public static bool IsLock(String tableId, String fieldId, string owner)
{
return IsLocked(tableId, fieldId, owner).Status;
}
public static string GetLockedEntityTitle(string tableId)
{
if (EntityTitles.ContainsKey(tableId))
return EntityTitles[tableId];
return tableId;
}
private static bool IsObjectValid(string tableId, string fieldId, string timestamp)
{
var projectValid = true;
if (string.IsNullOrWhiteSpace(timestamp))
return projectValid;
using (var dbContext = new EnVisageEntities())
{
switch (tableId)
{
case "Project":
var stamp = timestamp.StringToByteArray();
var id = Guid.Parse(fieldId);
projectValid = dbContext.Projects.Count(t => t.Id == id && t.EditTimestamp == stamp) > 0;
break;
default:
break;
}
}
return projectValid;
}
private static Dictionary<string, Dictionary<Guid, string>> GetEntityTitles(Dictionary<string, IEnumerable<Guid>> entities)
{
if (entities == null || !entities.Any())
return new Dictionary<string, Dictionary<Guid, string>>();
var results = new System.Collections.Concurrent.ConcurrentDictionary<string, Dictionary<Guid, string>>();
entities.AsParallel().ForAll((pair) =>
{
using (var dbContext = new EnVisageEntities())
{
switch (pair.Key)
{
case "Project":
using (var manager = new ProjectManager(dbContext))
{
var items = manager.DataTable.Where(t => pair.Value.Contains(t.Id)).Select(t => new { t.Id, t.Name });
if (items != null && items.Any())
results.TryAdd(pair.Key, items.ToDictionary(t=>t.Id, v=>v.Name));
}
break;
case "Scenario":
using (var manager = new ScenarioManager(dbContext))
{
var items = manager.DataTable.Where(t => pair.Value.Contains(t.Id)).Select(t => new { t.Id, t.Name });
if (items != null && items.Any())
results.TryAdd(pair.Key, items.ToDictionary(t => t.Id, v => v.Name));
}
break;
case "Team":
using (var manager = new TeamManager(dbContext))
{
var items = manager.DataTable.Where(t => pair.Value.Contains(t.Id)).Select(t => new { t.Id, t.Name });
if (items != null && items.Any())
results.TryAdd(pair.Key, items.ToDictionary(t => t.Id, v => v.Name));
}
break;
default:
break;
}
}
});
return results.ToDictionary(k=>k.Key, v=>v.Value);
}
public static ContentLockerObjectListModel GetLockedObjects(int pageSize = 25, int startOffset = 0, string orderBy = "ReleaseTimeUTC", bool isAsc = false)
{
var usersCache = (new UsersCache()).Value;
var items = new List<ContentLockerObjectModel>();
foreach (var table in lockersList)
{
if (table.Value != null && table.Value.Any())
items.AddRange(table.Value.Select(t => new ContentLockerObjectModel
{
EntityId = Guid.Parse(t.Key),
EntityType = GetLockedEntityTitle(table.Key),
Owner = usersCache.FirstOrDefault(u => u.UserName.ToString() == t.Value.Owner)?.DisplayName,
ReleaseTimeUTC = t.Value.GetReleaseDateTime().ToUniversalTime()
}));
}
switch (orderBy)
{
case "Owner":
items = (isAsc ? items.OrderBy(t => t.Owner) : items.OrderByDescending(t => t.Owner)).ToList();
break;
default:
items = (isAsc ? items.OrderBy(t => t.ReleaseTimeUTC) : items.OrderByDescending(t => t.ReleaseTimeUTC)).ToList();
break;
}
var result = new ContentLockerObjectListModel
{
Data = items.Skip(startOffset).Take(pageSize),
Total = items.Count()
};
var titles = GetEntityTitles(result.Data.GroupBy(gr => gr.EntityType).ToDictionary(k => k.Key, v => v.Select(gr => gr.EntityId)));
foreach (var item in result.Data)
{
if (titles.ContainsKey(item.EntityType))
{
if (titles[item.EntityType].ContainsKey(item.EntityId))
item.EntityTitle = titles[item.EntityType][item.EntityId];
}
}
return result;
}
}
}