避免多个Web服务访问。怎么实现呢?

时间:2013-02-12 11:19:09

标签: c# web-services windows-8 winrt-xaml async-await

我正在尝试实现一个简单的Windows 8 C#XAML应用程序,其中有两个调用来访问单个Web服务,一个是从项目加载和显示数据,另一个是显示通知。 由于对同一个Web服务进行了两次调用,我希望如果已经对该服务进行了一次调用,则另一个调用应该等待,并在第一次调用时使用相同的响应。

我如何实现这种功能?我没有添加任何代码,因为我没有为此编写的代码。我只想先思考然后再编码。

请告诉我,我可以为这种项目结构寻求帮助吗?

3 个答案:

答案 0 :(得分:1)

您可以通过缓存当前正在下载的Task来执行此操作,如果存在缓存的Task,则不会再次开始下载:

private volatile Task<string> m_cachedWebServiceTask;

async Task<string> AccessWebServiceAsync()
{
    var task = m_cachedWebServiceTask;

    if (task == null)
        task = m_cachedWebServiceTask = DownloadFromWebServiceAsync();

    try
    {
        return await task;
    }
    finally
    {
        m_cachedWebServiceTask = null;
    }
}

请注意,此代码具有竞争条件:如果您同时拨打AccessWebServiceAsync()两次,则DownloadFromWebServiceAsync()将被调用两次的可能性很小。但由于这仅仅是一次优化,我认为这应该不是问题。如果您遇到问题,则需要通过锁定来保护对该字段的访问。

答案 1 :(得分:0)

由于我觉得这个问题需要进一步关注,而且它的解决方案仍然可以优化,我决定发布另一种方法。 OP主要是关于利用以下3个要求范围的问题:应用程序中的用户体验应用程序的内部要求以及 Web服务的加载有多个请求。

  1. 应用程序需要发出加载数据的初始请求。
  2. 当他要求时,用户希望以最新的更新获得结果。
  3. 另一方面,

    因此,管理非常短的时间内发生的事情它实际上是解决问题的方法。

    在客户端,Service1Client类:

    public partial class Service1Client
    {
        // default time buffer value
        int _timeBuffer = 100;
    
        // a time buffer used for returning the same response  
        public int TimeBuffer
        {
            get { return _timeBuffer; }
            set { _timeBuffer = value; }
        }
        // the start and end time of the web service request
        static DateTime _start, _end;
    
        // a volatile static variable to store the response in the buffering time
        volatile static string _response;
    
        // used for blocking other threads until the current thread finishes it's job
        object _locker = new object();
    
        public async Task<string> GetResponseData()
        {
            return await Task.Factory.StartNew<string>(() =>
            {
                lock (_locker)
                {
                    if (DateTime.Now >= _end.AddMilliseconds(TimeBuffer))
                    {
                        _start = DateTime.Now;
                        var async = GetDataAsync();
                        _response = async.Result;
                        _end = DateTime.Now;
                    }
                }
                return _response;
            });
        }
    }
    

    用于测试的控制台应用程序:

    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                var client = new ServiceReference1.Service1Client();
                client.TimeBuffer = 150;
                Console.WriteLine(client.GetResponseData().Result);
                if (Console.ReadKey().Key == ConsoleKey.Enter)
                    break;
            }
        }
    }
    

    作为评论,请注意,出于明确样本的原因,我决定将GetDate WCF服务方法的返回类型从DateTime更改为string

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetData();
    }
    
    public class Service1 : IService1
    {
        public string GetData()
        {
            System.Threading.Thread.Sleep(5000);
            return DateTime.Now.ToString();
        }
    }
    

答案 2 :(得分:-1)

对于您的场景,一个可行的想法是扩展服务类。

IService1接口定义:

[ServiceContract]
public interface IService1
{
    [OperationContract]
    DateTime GetData();
}

Service1类定义:

public class Service1 : IService1
{
    public DateTime GetData()
    {
        System.Threading.Thread.Sleep(5000);
        return DateTime.Now;
    }
}

在客户端,扩展Service1Client类定义并添加新方法:

public partial class Service1Client
{
    static bool _isOpen;

    static DateTime? _cachedResponse;
    object _locker = new object();

    public DateTime GetResponseData()
    {
        if (!_isOpen)
        {
            if (!_cachedResponse.HasValue)
            {
                lock (_locker)
                {
                    _isOpen = true;
                    _cachedResponse = GetData();
                    _isOpen = false;
                }
                return _cachedResponse.Value;
            }
            else
            {
                Task.Factory.StartNew<DateTime>(() =>
                {
                    lock (_locker)
                    {
                        _isOpen = true;
                        _cachedResponse = GetData();
                        _isOpen = false;
                    }
                    return _cachedResponse.Value;
                });
            }
        }
        return _cachedResponse.Value;
    }
}

测试它:

class Program
{
    static void Main(string[] args)
    {
        while (true)
        {
            var client = new ServiceReference1.Service1Client();
            Console.WriteLine(client.GetResponseData());
            if (Console.ReadKey().Key == ConsoleKey.Enter)
                break;
        }
    }
}