简体   繁体   English

MVVM + WCF异步回调

[英]MVVM + WCF Async callbacks

I have a WCF service (IMyService) that I wrap into a service (ICentralService), so that I have one central service that I can inject in my ViewModels. 我有一个WCF服务(IMyService),可以包装到一个服务(ICentralService)中,这样我就可以在ViewModels中注入一个中央服务。 This would give me the advantage of changing/adding things at one location just before the WCF service is called. 这将使我受益于在调用WCF服务之前在一个位置更改/添加内容的优点。

Now because I need to make an async wcf call, my viewmodel also needs to be async. 现在,因为我需要进行异步wcf调用,所以我的viewmodel也需要是异步的。 I'm have a callback from my viewmodel, but my CentralService also has its own callback to call the End... method. 我的视图模型有一个回调,但是CentralService也有自己的回调来调用End ...方法。

Question : what is the best way to pass my viewmodel-callback to the EndTest method in the central service, so that this EndTest method could notify the callback on the viewmodel? 问题 :将我的viewmodel-callback传递给中央服务中的EndTest方法的最佳方法是什么,以便该EndTest方法可以通知viewmodel上的回调?

Or maybe there is a better way? 还是有更好的方法? I could directly inject IMyService in my ViewModel but then I don't have a central location (CentralService) where I can manipulate/inject data before sending it to the server via WCF. 我可以在ViewModel中直接注入IMyService,但是我没有中央位置(CentralService),可以在其中通过WCF将数据发送到服务器之前对其进行操作/注入。

Note: I'm on .NET 4.0, can't use "await", I'm also using WCF IAsyncResult Model (server-side). 注意:我在.NET 4.0上,不能使用“等待”,我也在使用WCF IAsyncResult模型(服务器端)。

Code: 码:

[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
  }
}

UPDATE: I've managed to wrap my IMyService into my ICentralService and pass the result from WCF (IMyService) to my viewmodel via ICentralService. 更新:我设法将IMyService包装到我的ICentralService中,并将结果从WCF(IMyService)通过ICentralService传递到我的视图模型。

First attempt/idea , but this did not return my "dataResult" value to my viewmodel: 第一次尝试/想法 ,但这没有将我的“ dataResult”值返回给我的viewmodel:

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;
}

Second attempt (works): 第二次尝试(有效):

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;
}

I'm not sure if this is a good solution using the TaskCompletionSource, but for now it seems to works. 我不确定使用TaskCompletionSource这是否是一个好的解决方案,但目前看来似乎可行。 (Too bad I have to return a useless -1 dataResult value). (太糟糕了,我必须返回一个无用的-1 dataResult值)。

The second update looks pretty good, but I think you can use TaskFactory.FromAsync to avoid the TaskCompletionSource for now. 第二个更新看起来不错,但是我认为您可以使用TaskFactory.FromAsync暂时避免TaskCompletionSource。 Here is a reference page for FromAsync with examples. 这是FromAsync的参考页和示例。 I haven't tested this, but the method may look something like this: 我还没有测试过,但是方法可能看起来像这样:

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;
                });
    }
}

You could use lambdas: 您可以使用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 will be your IAsyncResult if needed. 如果需要,ar将是您的IAsyncResult。 You could even run multiple requests in parallel, because the callback variable will be in local scope for each of the parallel calls. 您甚至可以并行运行多个请求,因为回调变量将在每个并行调用的本地范围内。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM