我开发了下载管理器应用程序。该解决方案由两部分组成: 1)代表下载管理器本身的WCF服务。它可以同时执行多次下载。每次下载都在独立的TPL任务中执行。有关每次下载的信息由Downloader类表示。并且服务中有一个ObservableCollection实例用于存储所有现有下载(Downloader实例)。 2)WPF客户端,其(实时地)显示从WCF服务接收的下载状态信息,例如下载的字节数。 请参阅下面的合同接口的condenced版本和Downloader类的精简版本:
// "IDownloadManagerService.cs" file where service contracts are defined.
[ServiceContract]
public interface IDownloadManagerService
{
/// <summary>
/// Returns a collection of executing or waiting downloads.
/// </summary>
/// <returns>a collection of executing or waiting downloads</returns>
[OperationContract]
ObservableCollection<Downloader> GetDownloads();
}
/// <summary>
/// Represents information about status of specified internet-resource downloading.
/// </summary>
[DataContract]
public class Downloader : INotifyPropertyChanged
{
/// <summary>
/// Downloaded bytes quantity.
/// </summary>
private Int64 _downloadedBytesQuantity = 0;
/// <summary>
/// Gets or sets downloaded bytes quantity.
/// </summary>
[DataMember]
public Int64 DownloadedBytesQuantity
{
get { return this._downloadedBytesQuantity; }
set
{
if (value != this._downloadedBytesQuantity)
{
this._downloadedBytesQuantity = value;
NotifyPropertyChanged();
}
}
}
}
每次下载更改其DownloadedBytesQuantity属性时,我都希望将ObservableCollection从服务传输到客户端,以便传输必须实时执行。下面我显示了一个实现IDownloadManagerService接口的DownloadManagerService类的condenced版本:
/// <summary>
/// Implements IDownloadManagerService interface.
/// </summary>
public class DownloadManagerService : IDownloadManagerService
{
#region Fields
/// <summary>
/// Collection of executing or waiting downloads.
/// </summary>
private ObservableCollection<Downloader> _downloads = new ObservableCollection<Downloader>();
#endregion
#region Methods
/// <summary>
/// Returns the collection of existing downloads to client.
/// </summary>
/// <returns>Collection of existing downloads</returns>
public ObservableCollection<Downloader> GetDownloads()
{
return this._downloads;
}
#endregion
}
因此,每当此集合中包含的任何下载更改其DownloadedBytesQuantity属性的值时,我都有兴趣将_downloads ObservableCollection传输到客户端。客户端必须显示(在DataGrid中)收到的更改。我该怎么做?这可行吗?
答案 0 :(得分:1)
很难知道完全您正在寻找什么,但我会猜测。
您想要服务的方式自动告知客户端当前任何下载是否已更改其DownloadBytesQuantity
属性,因此客户端可以相应地更新其视图以告知用户。
是?大!没有?忽略以下答案。
免责声明 - 这尚未经过测试,仅仅是我的头脑。如果您编写代码并且IDE抱怨一堆东西,请不要太惊讶。希望它足以让你至少走上正确的轨道。
呼叫客户端的服务方式是使用回调。 WCF服务回调(与很多其他WCF的东西一样)可能变得非常复杂,因此你在网上找到的很多信息都可能是压倒性的。
基本上,您需要WCF 客户端来实现专用的回调接口。
public interface IDownloadManagerServiceCallback
{
[OperationContract]
void OnDownloadBytesQuantityChanged(Downloader downloader);
}
这最终会为您的服务提供一个端点,以通知客户端下次更改了给定的Downloader
。
您的客户端将实现此方法,可能会以某种方式更新UI ...
public class DownloadManager : IDownloadManagerServiceCallback
{
#region IDownloadManagerServiceCallback members...
public void OnDownloadBytesQuantityChanged(Downloader downloader)
{
// handle download status change here...
}
#endregion
}
现在,根据您生成(或创建)WCF服务客户端的方式,您应该拥有一个接受InstanceContext
实例的构造函数。
class DownloadManagerServiceClient : DuplexClientBase, IDownloadManagerService
{
public DownloadManagerServiceClient(InstanceContext instanceContext)
: base(instanceContext)
{
}
//...
}
这是您在创建DownloadManagerServiceClient
代理实例时应该使用的内容。
从上方扩展我的DownloadManager
...
public class DownloadManager : IDownloadManagerServiceCallback
{
#region IDownloadManagerServiceCallback members...
public void OnDownloadBytesQuantityChanged(Downloader downloader)
{
// handle download status change here...
}
#endregion
private IDownloadManagerService _proxy;
public IDownloadManagerService Proxy
{
get
{
if (_proxy == null)
{
// get the instance context from ourselves as we are the callback client
var instanceContext = new InstanceContext(this);
_proxy = new DownloadManagerServiceClient(instanceContext);
}
return _proxy;
}
}
}
尚未完成。服务器端的一些细微变化。
您的ServiceContract
需要使用已创建的CallbackContract
进行标记。
[ServiceContract(CallbackContract = typeof(IDownloadManagerServiceCallback)]
public interface IDownloadManagerService
{
[OperationContract]
ObservableCollection<Downloader> GetDownloads();
}
现在,在您的服务中,当客户端调用时,您可以保存它的回调上下文。
public class DownloadManagerService : IDownloadManagerService
{
// all the other stuff....
// NOTE: this assumes only one client calling in
private IDownloadManagerServiceCallback _callback;
public ObservableCollection<Downloader> GetDownloads()
{
// first store the clients callback context so we can notify it upon a change
_callback = OperationContext.Current.GetCallbackChannel();
return this.Downloads;
}
// this is just for demonstration purposes
// somehow you'll know when a Downloader.DownloadedBytesQuantity has changed and that
// is where this logic will go
private void OnDownloadChanged(Downloader downloader)
{
_callback.OnDownloadBytesQuantityChanged(downloader);
}
}
虽然看起来好像在这里发生了很多,但这实际上是一个非常不完整的例子。
您可能希望处理调用同一服务的多个不同客户端,如果通道出现故障,您可能需要输入一些try-catch块。