2012-02-16 1 views
1

У меня есть функция в моем приложении lightwitch, которое загружает CSV-файл с сайта, который я хочу переписать с использованием Rx-структуры и предоставить возможность синхронно называть его.Наблюдаемый и Webclient для получения csv

Ниже приведены фрагменты кода для старой и новой функции. Однако новая функция не работает, вызов ParseCSV никогда не происходит. Я хотел бы знать, почему и если существует лучшее решение, не стесняйтесь предоставлять.

Старый Код:

private void ObservableCollection<Data> collection; 
public ObservableCollection<Data> GetData(string url, ObservableCollection<Data> targetCollection) 
{ 

collection = targetCollection; 
if (!string.IsNullOrEmpty(url)) 
{ 
    WebClient wc = new WebClient(); 
    wc.OpenReadCompleted += new OpenReadCompletedEventHandler(OpenReadCompleted_ParseCSV); 
    wc.OpenReadAsync(new Uri(url)); 
} 
return collection; 
} 

private void OpenReadCompleted_ParseCSV(object sender, OpenReadCompletedEventArgs e) 
{ 

if (e.Error != null) return; 

var webClient = sender as WebClient; 
if (webClient == null) return; 

try 
{ 
    using (StreamReader reader = new StreamReader(e.Result)) 
    { 
     string contents = reader.ReadToEnd(); 
     ... 
    } 
} 
catch (Exception ex) 
{ 
    System.Diagnostics.Debug.WriteLine("Error parsing CSV!\n" + ex.Message); 
} 

}

Новый код (с Rx):

private void ObservableCollection<Data> collection; 
public ObservableCollection<Data> GetData(string url, ObservableCollection<Data> targetCollection) 
{ 
collection = targetCollection; 
if (!string.IsNullOrEmpty(url)) 
{ 
    var result = Observable.FromEventPattern<OpenReadCompletedEventHandler, OpenReadCompletedEventArgs> 
       (
        ev => webClient.OpenReadCompleted += ev, 
        ev => webClient.OpenReadCompleted -= ev 
       ) 
       .Select(o => o.EventArgs.Result) 
       .FirstOrDefault() 
       .ParseCSV(); 

    // Call the Async method 
    webClient.OpenReadAsync(new Uri(url)); 
} 
return collection; 
} 

private void ParseCSV(this Stream stream) 
{ 
try 
{ 
    using (StreamReader reader = new StreamReader(e.Result)) 
    { 
     string contents = reader.ReadToEnd(); 
     ... 
    } 
} 
catch (Exception ex) 
{ 
    System.Diagnostics.Debug.WriteLine("Unable to get history data!\n" + ex.Message); 
} 
} 
+0

Только быстрый намек - вы злоупотребляя Rx - все это должно быть сделано в одном запросе без 'FirstOrDefault' в поле зрения ... – Enigmativity

+0

Это моя первая попытка сделать синхронный вызов (идея от функционального Алхимия: Синхронизация Silverlight) – sysboard

+0

Rx все о том, чтобы быть асинхронным. Если вы попытаетесь сделать это синхронно, вы, скорее всего, получите тупики. Используете ли вы инструмент «LinqPad»? Если нет, вы должны скачать его. Это отлично подходит для использования Rx-кода в среде с «царапинами». – Enigmativity

ответ

1

Это трудно знать, что вы собираетесь (я думаю, что вы попытался заглушить код для публикации в StackOverflow и много потерялся в переводе), но я думаю, что есть немного t я бы об этом поменял.

Одна вещь, которую я заметил, это то, что вы анализируете результат на основной теме. Есть причины, вы можете сделать это, но вы могли бы рассмотреть это:

//Note the void here. Is your intention to return a new collection or contribute 
//to an existing one? I assumed the latter and changed the method to be more clear 
//that this method causes side effects. 
public void GetData(string url, ObservableCollection<Data> targetCollection) 
{ 
     var result = Observable 
      .FromEventPattern<OpenReadCompletedEventHandler, OpenReadCompletedEventArgs> 
      (
       ev => webClient.OpenReadCompleted += ev, 
       ev => webClient.OpenReadCompleted -= ev 
      ) 
      .Select(o => ParseCSV(o.EventArgs.Result)); 

    result.Subscribe(targetCollection.Add); 
    webClient.OpenReadAsync(new Uri(url)); 
} 

//This method now returns a Data object read from a Stream 
private static Data ParseCSV(Stream stream) 
{ 
    try 
    { 
     using (StreamReader reader = new StreamReader(stream)) 
     { 
      string contents = reader.ReadToEnd(); 
      //... 
      return data; 
     } 
    } 
    catch (Exception ex) 
    { 
     //Use Exception.ToString(). You get error and stack trace information 
     //For this error as well as any inner exceptions. Nice! 
     System.Diagnostics.Debug.WriteLine("Unable to get history data!\n" + ex.ToString()); 
    } 
    return null; 
} 

Здесь для каждого значения, которое возвращается из запроса WebClient (там будет только один), мы проектирование результата в ваш Data, а не выполнять преобразование за пределами наблюдаемого потока.

Я внес некоторые незначительные изменения в ваши методы. Мне не особенно нравится такой код с побочными эффектами (передача в коллекцию, чтобы внести свой вклад, похоже на вектор для ошибок), но я разрешу это. Кроме этого, я думаю, что это должно работать очень хорошо.

+0

Спасибо за предоставленное вами решение и задумчивые замечания. Для этого я отметил ваш ответ полезным. Это еще не полное решение для того, чтобы я хотел иметь синхронный вариант. Не могли бы вы добавить это? – sysboard

0

Улучшенный Ответ на WPF

Создайте новый проект и вставьте этот код в MainWindow. Добавьте кнопку с именем XBStart, и все вы настроены, если вы подключите обработчик кликов к XBStart_Click. Запустите проект и нажмите кнопку!

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
     RX.DataReady += new RX.OnData(RX_DataReady); 
    } 

    private void RX_DataReady(ObservableCollection<string> Data) 
    { 
     Debugger.Break(); 
    } 

    private void XBStart_Click(object sender, RoutedEventArgs e) 
    { 
     RX.GetData("http://www.yahoo.com"); 
    } 
} 

public static class RX 
{ 
    public delegate void OnData(ObservableCollection<string> Data); 

    public static event OnData DataReady; 

    private static WebClient webClient; 

    private static ObservableCollection<string> TheData { get; set; } 

    private static void Notify() 
    { 
     if (DataReady != null) 
     { 
      DataReady(TheData); 
     } 
    } 

    public static void GetData(string url) 
    { 
     webClient = new WebClient(); 
     TheData = new ObservableCollection<string>(); 
     var result = Observable 
      .FromEventPattern<OpenReadCompletedEventHandler, 
           OpenReadCompletedEventArgs> 
      (
       ev => webClient.OpenReadCompleted += ev, 
       ev => webClient.OpenReadCompleted -= ev 
      ) 
      .Select(o => Parse.CSV(o.EventArgs.Result)); 

     result.Subscribe<string>(p => 
     { 
      TheData.Add(p); 
      Notify(); 
     }); 
     webClient.OpenReadAsync(new Uri(url)); 
    } 
} 

public static class Parse 
{ 
    //This method now returns a Data object read from a Stream 
    public static string CSV(Stream stream) 
    { 
     try 
     { 
      using (StreamReader reader = new StreamReader(stream)) 
      { 
       string contents = reader.ReadToEnd(); 
       //... 
       return contents; 
      } 
     } 
     catch (Exception ex) 
     { 
      //Use Exception.ToString(). 
      //You get error and stack trace information 
      //For this error as well as any inner exceptions. Nice! 
      System.Diagnostics.Debug.WriteLine("Unable to get history data!\n" + 
               ex.ToString()); 
     } 
     return null; 
    } 
} 
+0

Все это сообщение сделано, это взять существующий ответ и поместить его в код окна WPF. ОП совсем не спрашивал о WPF, и это добавило * ничего *, связанного с вопросом, который был задан. –

+0

Правда, но это то, что название сообщения указано «Улучшенный ответ для WPF». Я сделал это, чтобы помочь другим (включая меня), которые имеют такую ​​же проблему на земле WPF! –

Смежные вопросы