IIssueService
, в который перенести методы AcceptToWork
, Close
и т.д., которые будут принимать на вход ту Issue, над которой выполняется действие.IIssueService
различными менеджерами типа IConfigurationManager
и IDateTimeProvider
. Authorize
.class ApiAccess : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext == null)
{
throw new ArgumentNullException("actionContext");
}
if (!this.IsAuthorized(actionContext))
{
return;
}
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
bool isAuthroized = base.IsAuthorized(actionContext);
// логика проверки доступа
IEnumerable<string> authItems;
if (actionContext.Request.Headers.TryGetValues("Authorization", out authItems))
{
var auth = authItems.First().Split(' ');
var token = service.GetToken(auth.Last());
// ...
}
return isAuthroized;
}
}
[ApiAccess]
public class FileServerController : ApiController
{
// ...
}
let url = '/методAPI';
let data = {}; // параметры запроса
let headers = {
'Authorization': 'ANYNAMEHERE ' + sessionStorage.getItem('token')
};
$.ajax({
cache: false,
processData: false,
type: 'POST',
url: url,
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(data),
headers: headers,
success: (result) => {
// успех
},
error: (x, textStatus, errorThrown) => {
// ошибка
// на сервер можно сделать исключение для плохих маркеров доступа
// и проверить, если responseText содержит данный тип исключения,
// то требовать у пользователя повторную авторизацию
if (x.responseText) {
let exception = JSON.parse(x.responseText);
// AccessDeniedException - тип исключения в WebAPI,
// (скорее всего полное имя типа придется указывать)
if (exception.ExceptionType == 'AccessDeniedException') {
// ...
}
}
}
});
_inner
используется у вас во многих местах, и есть некий публичный метод, который его возвращает. Тогда внешний код сможет вызвать этот метод, получить тот же экземпляр объекта _inner
и поставить на него свой lock
. Так можно случайно словить взаимоблокировку и повесить приложение._lock
- это просто правило хорошего тона, его создают для безопасности: очень маловероятно, что кому-то придет в голову использовать его за пределами инструкции lock
и тем более вернуть из метода.internal class TemporaryObject
{
private static long _counter = 0;
public long Key { get; private set; }
public TemporaryObject()
{
Key = Interlocked.Increment(ref _counter);
}
/// <summary>
/// Событие при завершении ожидания
/// </summary>
public Action Callback;
/// <summary>
/// Срок истечения ожидания
/// </summary>
public DateTime ExpirationDate;
/// <summary>
/// Следующий объект с ближайшей датой окончания ожидания
/// </summary>
public TemporaryObject Next;
}
public class TemporaryObjectPool
{
private readonly object _locker = new object();
/// <summary>
/// Таймер. Один на всех
/// </summary>
private Timer _timer;
/// <summary>
/// Объект с ближайшей датой окончания ожидания
/// </summary>
private TemporaryObject _current = null;
/// <summary>
/// Переустановка таймера
/// </summary>
private void ResetTimer()
{
if (null != _current)
{
var diff = (_current.ExpirationDate - DateTime.Now).TotalMilliseconds;
if (diff < 0) diff = 0;
_timer.Change((int)diff, Timeout.Infinite);
}
else
{
_timer.Change(Timeout.Infinite, Timeout.Infinite);
}
}
public TemporaryObjectPool()
{
_timer = new Timer(state =>
{
Action action = null;
lock (_locker)
{
if (null != _current)
{
// Получаем событие для исполнения
action = _current.Callback;
// Находим следующий ожидающий объект
_current = _current.Next;
// Перезадание таймера
ResetTimer();
}
}
// Вызов события ожидавшего даты
if (null != action)
{
ThreadPool.QueueUserWorkItem(s => action());
}
}, null, Timeout.Infinite, Timeout.Infinite);
}
/// <summary>
/// Добавление ожидающего объекта
/// </summary>
/// <param name="insert"></param>
internal long Push(TemporaryObject insert)
{
lock (_locker)
{
// Если пул пуст, то добавляемое событие становится корневым
if (null == _current)
{
_current = insert;
}
else
{
// Если пул не пуст
var cursor = _current;
TemporaryObject prev = null;
// Поиск места для вставки, сложность вставки O(n) в худшем случае
do
{
if (DateTime.Compare(cursor.ExpirationDate, insert.ExpirationDate) > 0)
{
insert.Next = cursor;
if (null == prev)
{
_current = insert;
}
else
{
prev.Next = insert;
}
break;
}
prev = cursor;
cursor = cursor.Next;
if (cursor == null)
{
prev.Next = insert;
}
} while (cursor != null);
}
ResetTimer();
}
return insert.Key;
}
public void Remove(long key)
{
lock (_locker)
{
if (_current == null) return;
bool removed = false;
if (_current.Key == key)
{
_current = _current.Next;
removed = true;
}
else
{
var prev = _current;
var next = _current.Next;
while (next != null)
{
if (next.Key == key)
{
prev.Next = next.Next;
removed = true;
break;
}
prev = next;
next = next.Next;
}
}
if (removed)
{
ResetTimer();
}
}
}
}
var pool = new TemporaryObjectPool();
pool.Push(new TemporaryObject { Callback = () => Console.WriteLine("#1 removed"), ExpirationDate = DateTime.Now.AddSeconds(5) });
pool.Push(new TemporaryObject { Callback = () => Console.WriteLine("#2 removed"), ExpirationDate = DateTime.Now.AddSeconds(10) });
pool.Push(new TemporaryObject { Callback = () => Console.WriteLine("#3 removed"), ExpirationDate = DateTime.Now.AddSeconds(15) });