如何使用Reactive Extensions在域服务中调用Silverlight RIA InvokeOperation?

时间:2011-11-25 18:25:51

标签: silverlight system.reactive invoke

有谁知道如何使用Reactive Extensions调用RIA InvokeOperation?一旦调用全部完成,我需要处理来自Silverlight中几个异步调用的一些数据。在我的测试中,我将一些字符串与查询结果相结合,现在需要添加来自调用RIA域服务调用方法的结果,并且卡住了。

我在RIA域服务端的简单测试功能(完成非RX风格)如下所示:

    [Invoke]
    public string DomainServiceFunction()
    {
        return "hello";
    }

在客户端,这个老派的代码调用了这个方法,并且是我想用RX实现的部分:

    private void CallDomainServiceFunction(object sender, RoutedEventArgs e)
    {
        DomainService1 DomainContext = new DomainService1();   
        InvokeOperation invokeOp = DomainContext.DomainServiceFunction(OnInvokeCompleted, null);
    }

    private void OnInvokeCompleted(InvokeOperation invOp)
    {
        Debug.WriteLine(invOp.Value);//This prints "hello".
    }

我编写了一些测试代码,它结合了来自多个来源的数据(这也是我想要添加RIA InvokeOperation调用的地方)。它是由几个字符串和一个查询返回的实体组成的元组:

    private void PrintProperty1()
    {
        GISDomainContext DomainContext = new GISDomainContext();
        //Query the database to get information for a property using the folio number.
        var loadProperty = from loadOperation in DomainContextExtensions
              .LoadAsync(DomainContext, DomainContext.GetPropertyNoOwnersByFolioQuery("19401006.000"))
                            select loadOperation;
        //Zip up the property entity with a string for testing purposes.
        var Data = Observable.Return("a bit of text ")
            .Zip((Observable.Return("some more text")
            .Zip(loadProperty, (a, b) => Tuple.Create(a, b))), (a,b) => Tuple.Create(a,b));
        //Add a bit more stuff just to show it can be done.

        //THIS IS WHERE I WOULD ALSO ZIP WITH THE VALUE RETURNED FROM AN InvokeOperation.

        Data.Subscribe
        (
            //When all the required data are prepared then proceed...
            r => //On Next 
            {
                Debug.WriteLine("OnNext: " + r.Item1 + ", " + r.Item2.Item1 + ", " + r.Item2.Item2.Entities.First().folio.ToString());
                //results: "OnNext: a bit of text , some more text, 19401006.000"
                //At this point the data are all now available for further processing.
            },
            r => //On Error
            {
                Debug.WriteLine("Error in PrintProperty1: " + r.ToString());
            },
            () =>//On Completed
            {
                Debug.WriteLine("Completed PrintProperty1");
            }
        );
    }

我怀疑FromAsyncPattern是关键,但显然Silverlight隐藏了FromAsyncPattern期望作为参数的开始/结束调用

引自here

  

“Silverlight的一个重要说明!

     

Silverlight的网络服务   生成的客户端代码做了一些有点烦人的事 - 它隐藏起来了   BeginXXXX / EndXXXX调用,大概是为了制作Intellisense   清洁器。然而,他们没有消失,你可以让他们回来的方式是   通过将MyCoolServiceClient对象强制转换为其底层接口   (即LanguageServiceClient对象已生成   它实现的ILanguageServiceClient接口)“

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

实际上,Silverlight并没有隐藏任何东西。这些方法不存在于由RIA服务工具生成的DomainContext派生代理上。

但是这是一个将Invoke操作包装到IObservable的扩展方法:

public static class DomainContextExtensions
{
  // The method takes in an invoke operation proxy method delegate
  // and returns an observable sequence factory
  public static Func<T1, IObservable<TResult>> FromInvokeOperation<T1, TResult>
    (Func<T1, Action<InvokeOperation<TResult>>, object, InvokeOperation<TResult>> operationDelegate)
  {
    Contract.Requires<ArgumentNullException>(operationDelegate != null, "operationDelegate");
    Contract.Ensures(Contract.Result<Func<T1, IObservable<TResult>>>() != null);

    return x1 =>
      {
        // the subject is a storage for the result.
        var subject = new AsyncSubject<TResult>();

        try
        {
          var invokeOperation = operationDelegate(x1, operation =>
            {
              // handle operation results

              if (operation.IsCanceled)
              {
                return;
              }

              if (operation.HasError)
              {
                subject.OnError(operation.Error);
                operation.MarkErrorAsHandled();
                return;
              }

              Contract.Assume(operation.IsComplete);
              subject.OnNext(operation.Value);
              subject.OnCompleted();
            }, null);

          // create the operation cancellation object
          var invokeOperationCancellation = Disposable.Create(() =>
          {
            // need to check if the operation has completed before the subscription is disposed
            if (!invokeOperation.IsComplete && invokeOperation.CanCancel)
            invokeOperation.Cancel(); // this might abort the web call to save bandwidth
          });

          // construct a new observable that adds invoke operation cancellation upon dispose
          return Observable.Create<TResult>(obs => new CompositeDisposable(invokeOperationCancellation, subject.Subscribe(obs)));
        }
        catch (Exception ex)
        {
          return Observable.Create<TResult>(obs =>
            {
              obs.OnError(ex);
              return Disposable.Empty;
            });
        }
      };
  }
}

这应该有用,虽然我没有测试过。

用法:

var context = ... // get your DomainContext
var param = ... // operation parameter
// This will create the observable:
var o = DomainContextExtensions.FromInvokeOperation</*Parameter type goes here*/, /*Result type goes here*/>(context.YourOperationMethod)(param);
o.Subscribe(...); // subscribe as you wish or build a query

您必须编写其他方法来支持具有不同数量参数的调用操作。