Как вызвать вызов Silverlight RIA InvokeOperation в доменной службе с использованием реактивных расширений? - PullRequest
0 голосов
/ 25 ноября 2011

Кто-нибудь знает, как я могу вызвать RIA InvokeOperation с использованием Reactive Extensions?Мне нужно обработать некоторые данные, поступающие от нескольких асинхронных вызовов в Silverlight, когда все вызовы завершены.В моем тесте я объединил пару строк с результатами запроса, и теперь мне нужно добавить результаты вызова метода RIA Domain Service Invoke, и я застрял.

Моя простая функция тестана стороне службы домена 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 скрывает вызовы Begin / End, которые FromAsyncPatternожидает в качестве параметров

Цитируется здесь :

"Важное примечание для Silverlight!

Сгенерированный веб-сервисом Silverlight код клиента делает что-тоНемного раздражает - он скрывает вызовы BeginXXXX / EndXXXX, предположительно, чтобы сделать Intellisense чище. Однако они не исчезли, но вы можете получить их обратно, приведя объект MyCoolServiceClient к своему базовому интерфейсу (то есть объект LanguageServiceClient имеетсгенерированный интерфейс ILanguageServiceClient, который он реализует) "

Есть предложения?

1 Ответ

1 голос
/ 30 ноября 2011

На самом деле Silverlight ничего не скрывает. Методы просто не существуют на DomainContext -производном прокси, созданном инструментами RIA Services.

Но вот метод расширения, который превращает операцию 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

Вам потребуется написать дополнительные методы для поддержки операций вызова с различным количеством параметров.

...