MVVM + WCF 异步回调
MVVM + WCF Async callbacks
我有一个 WCF 服务 (IMyService),我将其包装到一个服务 (ICentralService) 中,这样我就有了一个可以注入到我的 ViewModel 中的中央服务。这将使我在调用 WCF 服务之前在一个位置获得 changing/adding 东西的优势。
现在因为我需要进行异步 wcf 调用,所以我的视图模型也需要是异步的。我有一个来自我的视图模型的回调,但我的 CentralService 也有自己的回调来调用 End... 方法。
问题:将我的 viewmodel-callback 传递给中央服务中的 EndTest 方法的最佳方式是什么,以便此 EndTest 方法可以通知 viewmodel 上的回调?
或者也许有更好的方法?
我可以直接在我的 ViewModel 中注入 IMyService,但是我没有中心位置 (CentralService),我可以在通过 WCF 将数据发送到服务器之前 manipulate/inject 数据。
注意:我在 .NET 4.0 上,无法使用 "await",我也在使用 WCF IAsyncResult 模型(服务器端)。
代码:
[ServiceContract(....)]
public interface IMyService {
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginTest(int x, AsyncCallback, object state);
int EndTest(IAsyncResult result);
}
public interface ICentralService {
void WorkItAsync(int x, AsyncCallback callback);
}
public class CentralService : ICentralService
{
private IMyService _Srv;
public CentralService(IMyService srv)
{
_Srv = srv;
}
public void WorkItAsync(int x, AsyncCallback callback)
{
// callback is the callback from my viewmodel
_Srv.BeginTest(x, new AsyncCallback(WorkItCompleted));
}
private void WorkItCompleted(IAsyncResult r)
{
// ...
int result = _Srv.EndTest(r);
// Need to call callback from viewmodel now to notify it is ready.
}
}
public class SomeViewModel : INotifyPropertyChanged
{
private ICentralService _Central;
public SomeViewModel(ICentralService central) {
_Central = central;
}
private void A() {
_Central.WorkItAsync(5, new AsyncCallback(B));
}
private void B(object test) {
// do something with the result
}
}
更新:
我已经设法将我的 IMyService 包装到我的 ICentralService 中,并通过 ICentralService 将结果从 WCF (IMyService) 传递到我的视图模型。
第一个 attempt/idea,但这并没有 return 我的 "dataResult" 对我的视图模型的价值:
public void WorkItAsync(int x, AsyncCallback callback)
{
var task = Task<int>.Factory.StartNew(() =>
{
int dataResult = -1;
_Srv.BeginTest(x, (ar) => {
dataResult = _Srv.EndTest(ar);
}, null);
return dataResult ;
});
if (callback != null)
task.ContinueWith((t) => callback(t));
return task;
}
第二次尝试(成功):
public void WorkItAsync(int x, AsyncCallback callback)
{
TaskCompletionSource<int> tcs1 = new TaskCompletionSource<int>();
Task<int> t1 = tcs1.Task;
Task<int>.Factory.StartNew(() =>
{
int dataResult = -1;
_Srv.BeginTest(x, (ar) => {
dataResult = _Srv.EndTest(ar);
tcs1.SetResult(dataResult);
}, null);
return dataResult;
});
if (callback != null)
t1.ContinueWith((t) => callback(t));
return t1;
}
我不确定这是否是一个使用 TaskCompletionSource 的好解决方案,但目前它似乎可行。 (太糟糕了,我不得不 return 一个无用的 -1 dataResult 值)。
你可以使用 lambdas:
public void WorkItAsync(int x, AsyncCallback callback)
{
// callback is the callback from my viewmodel
_Srv.BeginTest(x, ar=>{
int result = _Srv.EndTest(ar);
callback(ar);
});
}
ar 将是您的 IAsyncResult(如果需要)。您甚至可以 运行 多个并行请求,因为回调变量将在每个并行调用的本地范围内。
第二个更新看起来很不错,但我认为您可以使用 TaskFactory.FromAsync to avoid the TaskCompletionSource for now. Here is a reference page 作为 FromAsync 的示例。我还没有测试过这个,但是这个方法可能看起来像这样:
public interface ICentralService
{
// Just use .ContinueWith to call a completion method
Task<int> WorkItAsync(int x);
}
public class CentralService : ICentralService
{
private IMyService _Srv;
public CentralService(IMyService srv)
{
_Srv = srv;
}
public Task<int> WorkItAsync(int x)
{
// Callback is handled in ViewModel using ContinueWith
return Task<int>.Factory.FromAsync(_Src.BeginTest, _Src.EndTest, x);
}
}
public class SomeViewModel : INotifyPropertyChanged
{
private ICentralService _Central;
public SomeViewModel(ICentralService central)
{
_Central = central;
}
private void A()
{
_Central.WorkItAsync(5)
.ContinueWith(prevTask =>
{
// Handle or throw exception - change as you see necessary
if (prevTask.Exception != null)
throw prevTask.Exception;
// Do something with the result, call another method, or return it...
return prevTask.Result;
});
}
}
我有一个 WCF 服务 (IMyService),我将其包装到一个服务 (ICentralService) 中,这样我就有了一个可以注入到我的 ViewModel 中的中央服务。这将使我在调用 WCF 服务之前在一个位置获得 changing/adding 东西的优势。
现在因为我需要进行异步 wcf 调用,所以我的视图模型也需要是异步的。我有一个来自我的视图模型的回调,但我的 CentralService 也有自己的回调来调用 End... 方法。
问题:将我的 viewmodel-callback 传递给中央服务中的 EndTest 方法的最佳方式是什么,以便此 EndTest 方法可以通知 viewmodel 上的回调?
或者也许有更好的方法? 我可以直接在我的 ViewModel 中注入 IMyService,但是我没有中心位置 (CentralService),我可以在通过 WCF 将数据发送到服务器之前 manipulate/inject 数据。
注意:我在 .NET 4.0 上,无法使用 "await",我也在使用 WCF IAsyncResult 模型(服务器端)。
代码:
[ServiceContract(....)]
public interface IMyService {
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginTest(int x, AsyncCallback, object state);
int EndTest(IAsyncResult result);
}
public interface ICentralService {
void WorkItAsync(int x, AsyncCallback callback);
}
public class CentralService : ICentralService
{
private IMyService _Srv;
public CentralService(IMyService srv)
{
_Srv = srv;
}
public void WorkItAsync(int x, AsyncCallback callback)
{
// callback is the callback from my viewmodel
_Srv.BeginTest(x, new AsyncCallback(WorkItCompleted));
}
private void WorkItCompleted(IAsyncResult r)
{
// ...
int result = _Srv.EndTest(r);
// Need to call callback from viewmodel now to notify it is ready.
}
}
public class SomeViewModel : INotifyPropertyChanged
{
private ICentralService _Central;
public SomeViewModel(ICentralService central) {
_Central = central;
}
private void A() {
_Central.WorkItAsync(5, new AsyncCallback(B));
}
private void B(object test) {
// do something with the result
}
}
更新: 我已经设法将我的 IMyService 包装到我的 ICentralService 中,并通过 ICentralService 将结果从 WCF (IMyService) 传递到我的视图模型。
第一个 attempt/idea,但这并没有 return 我的 "dataResult" 对我的视图模型的价值:
public void WorkItAsync(int x, AsyncCallback callback)
{
var task = Task<int>.Factory.StartNew(() =>
{
int dataResult = -1;
_Srv.BeginTest(x, (ar) => {
dataResult = _Srv.EndTest(ar);
}, null);
return dataResult ;
});
if (callback != null)
task.ContinueWith((t) => callback(t));
return task;
}
第二次尝试(成功):
public void WorkItAsync(int x, AsyncCallback callback)
{
TaskCompletionSource<int> tcs1 = new TaskCompletionSource<int>();
Task<int> t1 = tcs1.Task;
Task<int>.Factory.StartNew(() =>
{
int dataResult = -1;
_Srv.BeginTest(x, (ar) => {
dataResult = _Srv.EndTest(ar);
tcs1.SetResult(dataResult);
}, null);
return dataResult;
});
if (callback != null)
t1.ContinueWith((t) => callback(t));
return t1;
}
我不确定这是否是一个使用 TaskCompletionSource 的好解决方案,但目前它似乎可行。 (太糟糕了,我不得不 return 一个无用的 -1 dataResult 值)。
你可以使用 lambdas:
public void WorkItAsync(int x, AsyncCallback callback)
{
// callback is the callback from my viewmodel
_Srv.BeginTest(x, ar=>{
int result = _Srv.EndTest(ar);
callback(ar);
});
}
ar 将是您的 IAsyncResult(如果需要)。您甚至可以 运行 多个并行请求,因为回调变量将在每个并行调用的本地范围内。
第二个更新看起来很不错,但我认为您可以使用 TaskFactory.FromAsync to avoid the TaskCompletionSource for now. Here is a reference page 作为 FromAsync 的示例。我还没有测试过这个,但是这个方法可能看起来像这样:
public interface ICentralService
{
// Just use .ContinueWith to call a completion method
Task<int> WorkItAsync(int x);
}
public class CentralService : ICentralService
{
private IMyService _Srv;
public CentralService(IMyService srv)
{
_Srv = srv;
}
public Task<int> WorkItAsync(int x)
{
// Callback is handled in ViewModel using ContinueWith
return Task<int>.Factory.FromAsync(_Src.BeginTest, _Src.EndTest, x);
}
}
public class SomeViewModel : INotifyPropertyChanged
{
private ICentralService _Central;
public SomeViewModel(ICentralService central)
{
_Central = central;
}
private void A()
{
_Central.WorkItAsync(5)
.ContinueWith(prevTask =>
{
// Handle or throw exception - change as you see necessary
if (prevTask.Exception != null)
throw prevTask.Exception;
// Do something with the result, call another method, or return it...
return prevTask.Result;
});
}
}