Наблюдаемый и Webclient, чтобы получить CSV - PullRequest
2 голосов
/ 16 февраля 2012

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

Ниже приведены фрагменты кода для старой и новой функции. Новая функция, однако, не работает, вызов 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);
}
}

Ответы [ 2 ]

2 голосов
/ 17 февраля 2012

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

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

//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 голосов
/ 13 июля 2012

Улучшенный ответ для WPF

Создайте новый проект и вставьте этот код в главное окно.Добавьте кнопку с именем 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;
    }
}
...