إنشاء واجهة برمجة تطبيقات عميل REST باستخدام ملحقات تفاعلية (RX)
-
26-09-2019 - |
سؤال
أحاول الحصول على رأسي حول حالات الاستخدام الصحيحة للملحقات التفاعلية (RX). الأمثلة التي تستمر في الظهور هي أحداث واجهة المستخدم (السحب والإسقاط ، الرسم) ، والاقتراحات بأن RX مناسبة للتطبيقات/العمليات غير المتزامنة مثل مكالمات خدمة الويب.
أنا أعمل على تطبيق حيث أحتاج إلى كتابة واجهة برمجة تطبيقات عميل صغيرة لخدمة REST. أحتاج إلى استدعاء أربع نقاط نهائية للراحة ، وثلاثة للحصول على بعض البيانات المرجعية (المطارات وشركات الطيران والأوضع) ، والرابع هي الخدمة الرئيسية التي ستمنحك أوقات الرحلة لمطار معين.
لقد أنشأت فصولًا تعرض خدمات البيانات المرجعية الثلاث ، والأساليب تبدو مثل هذا:
public Observable<IEnumerable<Airport>> GetAirports()
public Observable<IEnumerable<Airline>> GetAirlines()
public Observable<IEnumerable<Status>> GetStatuses()
public Observable<IEnumerable<Flights>> GetFlights(string airport)
في طريقة GetFlights الخاصة بي ، أريد أن تعقد كل رحلة مرجعًا للمطار الذي تغادر منه ، وشركة الطيران التي تعمل في الرحلة. للقيام بذلك ، أحتاج إلى أن تكون البيانات من GetAirports و GetAirlines متاحة. ستتم إضافة كل مطار وشركة طيران وحالة إلى Dictionar (أي قاموس) حتى أتمكن بسهولة من ضبط المرجع عند تحليل كل رحلة.
flight.Airport = _airports[flightNode.Attribute("airport").Value]
flight.Airline = _airlines[flightNode.Attribute("airline").Value]
flight.Status = _statuses[flightNode.Attribute("status").Value]
يبدو أن عملي الحالي الآن:
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);
}
يعتمد التنفيذ الحالي على إجابة سيرجي ، ويستخدم ForkJoin لضمان التنفيذ المتسلسل وأن يتم تحميل البيانات المرجعية قبل الرحلات الجوية. هذا التنفيذ مخصص أكثر أناقة من الاضطرار إلى إطلاق حدث "محول" مثل "عملي السابق".
المحلول
أعتقد ، إذا كنت تتلقى قائمة بالكيانات من كل مكالمة راحة ، فيجب أن تحتوي مكالمتك على توقيع مختلف قليلاً - فأنت لا تراقب كل قيمة في مجموعة الإرجاع ، فأنت تراقب حدث إكمال المكالمة. لذلك بالنسبة للمطارات ، يجب أن يكون لها التوقيع:
public IObservable<Aiports> GetAirports()
تتمثل الخطوة التالية في تشغيل الثلاثة الأولى بالتوازي وانتظر كل منهم:
var ports_lines_statuses =
Observable.ForkJoin(GetAirports(), GetAirlines(), GetStatuses());
الخطوة الثالثة هي إنشاء ما سبق قابلاً للاستيعاب مع GetFlights ():
var decoratedFlights =
from pls in ports_lines_statuses
let airport = MyAirportFunc(pls)
from flight in GetFlights(airport)
select flight;
تحرير: ما زلت لا أفهم سبب عودة خدماتك
IObservable<Airport>
بدلاً من
IObservable<IEnumerable<Airport>>
AFAIK ، من الباقي اتصل عليك الحصول على جميع الكيانات في وقت واحد - ولكن ربما تقوم بالترحيل؟ على أي حال ، إذا كنت تريد أن تقوم RX بعمل التخزين المؤقت ، يمكنك استخدام .BufferWithCount ():
var allAirports = new AirportNamesService()
.GetAirports().BufferWithCount(int.MaxValue);
...
ثم يمكنك تطبيق forkjoin:
var ports_lines_statuses =
allAirports
.ForkJoin(allAirlines, PortsLinesSelector)
.ForkJoin(statuses, ...
سوف تحتوي PORTS_LINES_STATUSES على حدث واحد على الجدول الزمني والذي سيحتوي على جميع البيانات المرجعية.
تحرير: إليك واحدة أخرى ، باستخدام ListObservable الطازجة (أحدث إصدار فقط):
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());
}
نصائح أخرى
حالة الاستخدام هنا تعتمد على السحب - iEnumerable على ما يرام. إذا كنت تريد أن تقول ، قم بإخطار مكان وجود رحلة جديدة ، ثم لفت مكالمة قائمة على السحب داخل الملاحظة.