是否有理由让每个WCF服务都调用Async?
我和我的伙伴正在讨论这个问题。他想让每个WPF服务调用Async以避免锁定UI(它是桌面WPF应用程序)。我反对这个想法。在大多数情况下,我不觉得需要异步调用,并且在需要时,应该专门编写RequestingClass和DataManager以处理异步调用。
我的论点是,为所有内容设置回调的代码要多得多,而且非常令人困惑。我也认为这可能会导致性能下降,尽管我尚未对此进行验证。他的论点是,有时你会收到大量的数据并且它会锁定用户界面,并且设置这样的WCF调用并不是那么多工作(他也没有发现以下代码混淆)。 / p>
我们之前从未使用过WCF服务器,所以我想我会给他带来疑问,并在这里询问其他一些意见。
例如:
我的方式:
public override User GetById(int id)
{
return new User(service.GetUserById(id));
}
它锁定UI,UserDataManager和WCF服务通道,直到WCF服务器返回User DataTransferObject,但它易于理解并且快速编码。它将用于大多数WCF服务调用,除非它实际上期望获取数据的延迟,在这种情况下,DataManager将被设置为处理异步调用。
他的方式:
public override void GetById(int id, Action<UserGroup> callback = null)
{
// This is a queue of all callbacks waiting for a GetById request
if (AddToSelectbyIdQueue(id, callback))
return;
// Setup Async Call
var wrapper = new AsyncPatternWrapper<UserDTO>(
(cb, asyncState) => server.BeginGetUserById(id, cb, asyncState),
Global.Instance.Server.EndGetUserById);
// Hookup Callback
wrapper.ObserveOnDispatcher().Subscribe(GetByIdCompleted);
// Run Async Call
wrapper.Invoke();
}
private void GetByIdCompleted(UserDTO dto)
{
User user = new User(dto);
// This goes through the queue of callbacks waiting
// for this method to complete and executes them
RunSelectIdCallbacks(user.UserId, user);
}
基类的回调队列:
/// <summary>
/// Adds an item to the select queue, or a current fetch if there is one
/// </summary>
/// <param name="id">unique object identifier</param>
/// <param name="callback">callback to run</param>
/// <returns>False if it needs to be fetched, True if it is already being
/// fetched</returns>
protected virtual bool AddToSelectbyIdQueue(int id, Action<T> callback)
{
// If the id already exists we have a fetch function already going
if (_selectIdCallbacks.ContainsKey(id))
{
if(callback != null)
_selectIdCallbacks[id].Add(callback);
return true;
}
if (callback != null)
{
List<Action<T>> callbacks = new List<Action<T>> {callback};
_selectIdCallbacks.Add(id, callbacks);
}
return false;
}
/// <summary>
/// Executes callbacks meant for that object Id and removes them from the queue
/// </summary>
/// <param name="id">unique identifier</param>
/// <param name="data">Data for the callbacks</param>
protected virtual void RunSelectIdCallbacks(int id, T data)
{
if (_selectIdCallbacks.ContainsKey(id))
{
foreach (Action<T> callback in _selectIdCallbacks[id])
callback(data);
_selectIdCallbacks.Remove(id);
}
}
它不会锁定UI,DataManager或WCF服务频道,但会有很多额外的编码。
AsyncPatternWrapper无论如何都在我们的应用程序中。它允许我们进行异步WCF调用并订阅回调事件
修改 我们有一个包装器,我们可以从UI线程中使用它来包装任何DataManager调用。它在BackgroundWorker上执行Synchronous方法,并对结果执行回调。
大部分额外代码是为了防止锁定DataManager和WCF服务通道。
答案 0 :(得分:5)
你的伴侣是对的;你不应该阻止UI线程。
作为异步调用的替代方法,您还可以使用BackgroundWorker或ThreadPool在后台线程中进行同步调用。
答案 1 :(得分:2)
值得注意的一些事情:
任何WCF调用都可能会阻塞,直到超时时间,您应该考虑到这一点。如果在连接丢失的情况下阻止UI,则会导致糟糕的用户体验。例如,如果服务器陷入困境,呼叫也可能比平时运行得慢。
即使你没有为异步目的包装调用,你可能已经想要一些包含对代理的调用。这是因为代理上的任何调用都可能使通道处于故障状态,如果发生这种情况,您拥有来调用通道上的Abort(),否则您可能会泄漏资源。有关其他信息,请参阅this post和this other post。 WCF代理不能像普通类一样使用,如果要在生产场景中使用它们,则必然需要额外的包装。这在很大程度上是不可避免的,因为它是由远程通信要求引入的额外边界情况和不可预测的行为造成的。