我的应用程序使用客户端企业缓存;我想避免为每个可缓存的调用编写代码,并想知道是否有一个解决方案可以缓存WCF客户端调用,即使对于异步调用也是如此。
这可以通过WCF“行为”或其他方式完成吗?代码示例?
答案 0 :(得分:3)
我前几天在WCF服务客户端(DataServiceClient)上使用通用扩展方法执行了此操作。它使用Actions和Func来传递实际的ServiceClient调用。最终的客户端使用语法有点时髦(如果你不喜欢lambdas),但是这个方法执行FaultException / Abort包装和缓存:
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);
}
现在只需在Create / Update / Deletes上的Reads和Uncache上调用Cache:
// 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代理约定和标准缓存程序,你很高兴。首先创建所有通用包装器模板也是很多工作,但我只有两个参数,它帮助我的所有缓存操作共享一个函数签名(现在)。如果这对您有用或者您有任何改进,请告诉我。
答案 1 :(得分:0)
不幸的是,我认为你必须自己动手。我不相信WCF内置了客户端缓存机制。
this question的答案也可能有所帮助。
答案 2 :(得分:0)
与上述解决方案类似,请查看http://www.acorns.com.au/blog/?p=85(WCF服务上的PolicyInjection)。您可以指定策略以匹配您的服务名称。
答案 3 :(得分:-2)
如果您想要缓存而不必在每次服务调用上显式实现它,请考虑Policy Injection应用程序块中的缓存处理程序。您可以使用属性标记您的呼叫,策略注入块将为您处理缓存。