连接WCF客户端缓存?

时间:2009-04-07 18:32:30

标签: wcf caching

我的应用程序使用客户端企业缓存;我想避免为每个可缓存的调用编写代码,并想知道是否有一个解决方案可以缓存WCF客户端调用,即使对于异步调用也是如此。

这可以通过WCF“行为”或其他方式完成吗?代码示例?

4 个答案:

答案 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应用程序块中的缓存处理程序。您可以使用属性标记您的呼叫,策略注入块将为您处理缓存。

http://msdn.microsoft.com/en-us/library/cc511757.aspx