Кэширование в Windows Communication Foundation (WCF)

Те, кто уже начал промышленное использование Windows Communcation Foundation в своих разработках уже наверняка столкнулись с множеством проблем различного характера.

Одной из таких проблем является кэширование результатов выполнения операций в рамках сервиса.

Как оказалось решение данной проблемы не такая уж и сложная задача. Чтобы осуществить это необходимо создать и подменить стандартный Invoker. Для реализации своего Invoker-а необходимо создать новый класс и реализовать в нем интерфейс IOperationInvoker. При этом необходимо сохранить ссылку на "оригинальный" Invoker. В новоиспеченном Invoker-е нас интересует следующий метод:

public object Invoke(object instance, object[] inputs, out object[] outputs)

Переопределив его, мы можем контролировать процесс выполнения операции на сервисе. Самое простое что можно сделать - это вызвать метод Invoke "оригинального" Invoker-а. Но кто нам мешает сделать еще несколько манипуляций перед этим? Именно тут мы и реализуем логику кэширования. Для самого простого случая это выглядить примерно так:

public object Invoke(object instance, object[] inputs,
  out object[] outputs)
{
  string result = String.Empty;
  int invokeInputParam = (int)inputs[0];

  if (_cacheDictionary.ContainsKey(invokeInputParam) == true)
  {
    Console.WriteLine(@"Cached for param value: " + invokeInputParam.ToString());
    result = _cacheDictionary[invokeInputParam];
  }
  else
  {
    result = (string)_baseInvoker.Invoke(instance, inputs, out outputs);
    _cacheDictionary.Add(invokeInputParam, result);
  }

  outputs = new object[] { };
  return result;
}

После реализации Invoker-а необходимо каким-то образом "прикрутить" его к операции. Вариантов тут много. Один из них — создать класс-атрибут, в котором реализован интерфейс IOperationBehavior. Здесь, в методе ApplyDispatchBehavior как раз необходимо выполнить подмену Invoker-а:

public void ApplyDispatchBehavior(OperationDescription operationDescription,
  System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
{
  dispatchOperation.Invoker = new CacheInvoker(dispatchOperation.Invoker);
}

Осталось только навесить данный атрибут на операцию при реализации сервиса и все готово:

[Cache]
public string LongOperation(int a)
{
  System.Threading.Thread.Sleep(2000);
  return String.Format("Result {0} / DateTime: {1}", a, DateTime.Now);
}

Аналогичным образом можно организовать не только кэширование, но и различные другие операции. Например, формировать список выполненных операций и отправлять их на e-mail.