2010-05-14 15 views
10

Kafamı, Reaktif Uzantılar (Rx) için doğru kullanım durumları etrafında tutmaya çalışıyorum. Devam eden örnekler UI olayları (sürükle ve bırak, çizim) ve Rx'in web servis çağrıları gibi senkronize olmayan uygulamalar/işlemler için uygun olduğu önerileridir.Reaktif Uzantılar (Rx) kullanarak REST istemci API'si oluşturma

Bir REST hizmeti için küçük bir istemci API'si yazmam gereken bir uygulama üzerinde çalışıyorum. Bazı referans verilerini (Havaalanları, Havayolları ve Statüleri) almak için dört REST uç noktası, üçünü aramalıyım ve dördüncü, belirli bir havalimanı için uçuş sürelerini size verecek ana hizmettir.

Ben üç referans veri hizmetlerini açığa sınıflar oluşturduk ve yöntemler şuna benzer: Her Uçuş Havaalanı o kalkan oluyor bir başvuru tutmak istiyorum ve benim GetFlights yöntemde

public Observable<IEnumerable<Airport>> GetAirports() 
public Observable<IEnumerable<Airline>> GetAirlines() 
public Observable<IEnumerable<Status>> GetStatuses() 
public Observable<IEnumerable<Flights>> GetFlights(string airport) 

Uçuş çalışan havayolu şirketi. Bunu yapmak için GetAirports ve GetAirlines'ın mevcut olması gereken verilere ihtiyacım var. Her bir Uçuş, Havayolu ve Durum bir Dictionar'a (yani, Dictionary) eklenecektir, böylece her uçuşu ayrıştırırken referansı kolayca ayarlayabilirim.

public IObservable<IEnumerable<Flight>> GetFlightsFrom(Airport fromAirport) 
{ 
    var airports = new AirportNamesService().GetAirports(); 
    var airlines = new AirlineNamesService().GetAirlines(); 
    var statuses = new StatusService().GetStautses(); 


    var referenceData = airports 
     .ForkJoin(airlines, (allAirports, allAirlines) => 
          { 
           Airports.AddRange(allAirports); 
           Airlines.AddRange(allAirlines); 
           return new Unit(); 
          }) 
     .ForkJoin(statuses, (nothing, allStatuses) => 
          { 
           Statuses.AddRange(allStatuses); 
           return new Unit(); 
          }); 

    string url = string.Format(_serviceUrl, 1, 7, fromAirport.Code); 

    var flights = from data in referenceData 
        from flight in GetFlightsFrom(url) 
        select flight; 

    return flights; 
} 

private IObservable<IEnumerable<Flight>> GetFlightsFrom(string url) 
{ 
    return WebRequestFactory.GetData(new Uri(url), ParseFlightsXml); 
} 

şu anki uygulama Sergey'in cevap dayanmaktadır ve sıralı yürütülmesini sağlamak ForkJoin kullanır ve Veri Uçuşlar önce yüklenen başvuru olun:

flight.Airport = _airports[flightNode.Attribute("airport").Value] 
flight.Airline = _airlines[flightNode.Attribute("airline").Value] 
flight.Status = _statuses[flightNode.Attribute("status").Value] 

Benim şu anki uygulama şimdi buna benzer. Bu uygulama, önceki uygulamam gibi bir "ReferenceDataLoaded" olayını tetiklemekten daha zariftir.

+0

Yanıtı güncellendi - ayrıca, bu konuya bir göz atın: http://social.msdn.microsoft.com/Forums/en/rx/thread/20e9fea1-304f-4926-aa02-49ed558a84f5 - nasıl yazılacağını gösterir özel arabelleğe alma. –

cevap

2

Sanırım, her REST çağrısından bir varlıklar listesi alıyorsanız, aramanızın biraz farklı bir imzası olmalıdır - geri dönüş koleksiyonundaki her bir değeri gözlemlemiyorsanız, arama tamamlanma olayını gözlemliyorsunuz demektir.

public IObservable<Aiports> GetAirports() 

sonraki adım paralel ilk üç çalıştırmak ve hepsi beklemek olacaktır: havaalanları için, bu imzaya sahip olmalıdır Yani

var ports_lines_statuses = 
    Observable.ForkJoin(GetAirports(), GetAirlines(), GetStatuses()); 

üçüncü basamak oluşturmak için olmak woul GetFlights() ile abservable yukarıda:

var decoratedFlights = 
    from pls in ports_lines_statuses 
    let airport = MyAirportFunc(pls) 
    from flight in GetFlights(airport) 
    select flight; 

DÜZENLEME: hizmetlerinizi

dönmek hala neden anlamıyorum yerine

IObservable<IEnumerable<Airport>> 

afaik 10

IObservable<Airport> 

, REST tek seferde tüm varlıkları almak diyoruz - ama belki de sayfalama yapmak? hangi içerecektir

var ports_lines_statuses = 
    allAirports 
     .ForkJoin(allAirlines, PortsLinesSelector) 
     .ForkJoin(statuses, ... 

ports_lines_statuses zaman çizgisi üzerinde tek bir etkinliği içerecektir:

var allAirports = new AirportNamesService() 
     .GetAirports().BufferWithCount(int.MaxValue); 
... 

Sonra uygulayabilirsiniz ForkJoin: Eğer RX istiyorsanız Neyse, .BufferWithCount() kullanabilir tamponlama tüm referans verileri.

DÜZENLEME: İşte başka bir yeni basılmış ListObservable kullanarak, var (son sürümü için):

allAiports = airports.Start(); 
allAirlines = airlines.Start(); 
allStatuses = statuses.Start(); 

... 
whenReferenceDataLoaded = 
    Observable.Join(airports.WhenCompleted() 
       .And(airlines.WhenCompleted()) 
       .And(statuses.WhenCompleted()) 
       Then((p, l, s) => new Unit())); 



    public static IObservable<Unit> WhenCompleted<T>(this IObservable<T> source) 
    { 
     return source 
      .Materialize() 
      .Where(n => n.Kind == NotificationKind.OnCompleted) 
      .Select(_ => new Unit()); 
    } 
+0

Aslında tüm Havayolları, Havalimanları ve Statüsleri ilk olarak "tek parti" olarak almak istiyorum, çünkü Uçuşları aldığımda, mevcut olan üç referans veri kümesine ihtiyacım var, böylece onları bir Uçuşa bağlayabiliyorum. Bu yüzden Havaalanları bu Dictionart gibi bir dict içine almalıyım, böylece yapabileceğim: flight.Airport = havaalanları [flightXml.AirportCode]. –

+0

Soruyu yeni yöntem imzaları ile güncelledim. Haklısın, aslında bütün Hava Yolları, Havayolları ve Statüsleri bir seferde almak istiyorum. PortLinesSelector'da doğru muyum? Havalimanları ve havayolları birleştiren bir yöntemdir ve daha sonra ew sonucu ile önceki sonucu birleştirmek için ikinci bir yönteme ihtiyacım var? Silverlight 3/4 için en son RX sürümünü indirmeyi denedim, ancak Gözlemlenebilir üzerinde Start() yöntemini bulamadım (yalnızca başlangıç ​​ile). –

+0

@ jonas-folleso Evet, PortsLinesSelector (bağlantı noktaları, çizgiler) => yeni {portlar, çizgiler} gibi bir şeydir ve ikinci seçici bu sonuca üçüncü sonucu eklerdi. Buradaki fikir, mümkün olduğunca işlevsel stile sahip olmaya çalışmak ve sadece yerel değişkenleri kullanmak yerine verileri boru hattından geçirmektir. Gözlemlenebilir üzerinde Start() sadece .Net4 sürümünde, yani başkalarına taşınana kadar beklemeniz gerekir ... –

0

burada kullanım durumu bazlı çekme edilir - IEnumerable gayet iyi. Söylemek isterseniz, yeni bir uçuşun nereden geldiğini bildirin, ardından Observable.Generate içinde çekme temelli bir REST çağrısı sarmak bazı değerlerden olabilir.

+0

Rx, senaryoda bir REST istemcisi oluşturmak için iyi bir yaklaşım değil mi? Bu WP7 olduğundan eşzamanlı yapamıyorum, bu yüzden alternatif şöyle olurdu: GetAirlinesAsync ve GetAirlinesCompleted olayı var. Daha sonra GetAirlinesAsync, GetAirportsAsync ve GetStatusesAsync'i çağırmalı ve üç geri arama olayının GetFlights'ı aramadan önce yanmasını beklemeliyim ..? Ayrıca, her 3 dakikada bir yenilenmesi için GetFlights servisini tekrar aramak için yöntemimi genişletmeyi de planlıyordum. Bu nedenle, yeni uçuş nesnelerini gözlemlemek, iyi bir fikir gibi geliyor. –

+0

Temel API sadece uyumsuzsa, RX daha mantıklı. Observable.Generate ... –