Observable 和 Webclient 获取 csv

发布于 2025-01-05 18:22:04 字数 2166 浏览 1 评论 0原文

我的 lightswitch 应用程序中有一个函数,可以从网站下载 csv 文件,我想使用 Rx 框架重写该文件,并提供同步调用它的可能性。

下面提供了旧函数和新函数的代码片段。然而,新函数不起作用,对 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);
}
}

I have a function in my lightswitch application that downloads a csv file from a site which i want to re-write using Rx framework and provide provide possibility to call it synchronously.

Provide below are the code snippets for old and new function. The new function however doesn't work, the call to ParseCSV never happens. I would like to know why and if exists a better solution, feel free to provide.

Old Code:

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);
}

}

New Code (with 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);
}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

后知后觉 2025-01-12 18:22:04

很难知道你想要什么(我认为你试图简化在 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 类,而不是在可观察流之外进行转换。

我对你的方法做了一些小的修改。我不太喜欢这样带有副作用的代码(传递集合来贡献似乎是错误的向量),但我会允许它。除此之外,我认为这应该效果很好。

It's tough to know what you are going for (I think you tried to dumb down the code for posting on StackOverflow and a lot got lost in translation), but I think there are few things I would change about this.

One thing I noticed is that you are parsing the result back on the main thread. There are reasons you might do this, but you might consider this:

//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;
}

Here, for each value that comes back from the webClient request (there will only be one), we are projecting the result into your Data class, rather than doing the conversion outside of the observable stream.

I made some minor modifications to your methods. I don't particularly like code like this with side effects (passing in the collection to contribute to seems like a vector for bugs), but I'll allow it. Other than that, I think this should work pretty well.

清引 2025-01-12 18:22:04

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;
    }
}

An Improved Answer for WPF

Create a new project and paste this code in your MainWindow. Add a button named XBStart and you are all set if you hook up the click handler to XBStart_Click. Run the project and click the button!

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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文