Я сделал это на днях с помощью универсальных методов расширения на клиенте службы WCF (DataServiceClient). Он использует действия и функции для передачи фактических вызовов ServiceClient. Окончательный синтаксис использования клиента немного прикольный (если вам не нравятся лямбды), но этот метод выполняет FaultException / Abort wrapping AND caching:
public static class ProxyWrapper
{
// start with a void wrapper, no parameters
public static void Wrap(this DataServiceClient _svc, Action operation)
{
bool success = false;
try
{
_svc.Open();
operation.Invoke();
_svc.Close();
success = true;
}
finally
{
if (!success)
_svc.Abort();
}
}
// next, a void wrapper with one generic parameter
public static void Wrap<T>(this DataServiceClient _svc, Action<T> operation, T p1)
{
bool success = false;
try
{
_svc.Open();
operation.Invoke(p1);
_svc.Close();
success = true;
}
finally
{
if (!success)
_svc.Abort();
}
}
// non-void wrappers also work, but take Func instead of Action
public static TResult Wrap<T, TResult>(this DataServiceClient _svc, Func<T, TResult> operation, T p1)
{
TResult result = default(TResult);
bool success = false;
try
{
_svc.Open();
result = operation.Invoke(p1);
_svc.Close();
success = true;
}
finally
{
if (!success)
_svc.Abort();
}
return result;
}
}
На стороне клиента мы должны называть их так:
internal static DBUser GetUserData(User u)
{
DataServiceClient _svc = new DataServiceClient();
Func<int, DBUser> fun = (x) => _svc.GetUserById(x);
return _svc.Wrap<int, DBUser>(fun, u.UserId);
}
Видишь план здесь? Теперь, когда у нас есть общий набор оболочек для вызовов WCF, мы можем использовать ту же идею для внедрения некоторого кэширования. Здесь я использовал "низкотехнологичные" и просто начал разбрасывать строки для имени ключа кеша ... Вы можете сделать что-то более элегантное с отражением, без сомнения.
public static TResult Cache<TResult>(this DataServiceClient _svc, string key, Func<TResult> operation)
{
TResult result = (TResult)HttpRuntime.Cache.Get(key);
if (result != null)
return result;
bool success = false;
try
{
_svc.Open();
result = operation.Invoke();
_svc.Close();
success = true;
}
finally
{
if (!success)
_svc.Abort();
}
HttpRuntime.Cache.Insert(key, result);
return result;
}
// uncaching is just as easy
public static void Uncache<T>(this DataServiceClient _svc, string key, Action<T> operation, T p1)
{
bool success = false;
try
{
_svc.Open();
operation.Invoke(p1);
_svc.Close();
success = true;
}
finally
{
if (!success)
_svc.Abort();
}
HttpRuntime.Cache.Remove(key);
}
Теперь просто вызовите Cache при чтении и Uncache при создании / обновлении / удалении:
// note the parameterless lambda? this was the only tricky part.
public static IEnumerable<DBUser> GetAllDBUsers()
{
DataServiceClient _svc = new DataServiceClient();
Func<DBUser[]> fun = () => _svc.GetAllUsers();
return _svc.Cache<DBUser[]>("AllUsers", fun);
}
Мне нравится этот метод, потому что мне не нужно было ничего перекодировать на стороне сервера, только мои прокси-вызовы WCF (которые, по общему признанию, были немного хрупкими / вонючими, чтобы разбросаться повсюду).
Замените в своих собственных соглашениях прокси WCF и стандартных процедурах кэширования, и все готово. Сначала нужно создать много шаблонов-оберток, но я выбрал только два параметра, и это помогает всем моим операциям кэширования использовать одну сигнатуру функции (пока). Дайте мне знать, если это работает для вас или у вас есть какие-либо улучшения.