【问题标题】:Observable and Webclient to get csvObservable 和 Webclient 获取 csv
【发布时间】:2012-02-16 09:37:23
【问题描述】:

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

下面提供旧功能和新功能的代码sn-ps。然而,新功能不起作用,对 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);
}
}

【问题讨论】:

  • 只是一个快速提示 - 你在滥用 Rx - 这一切都应该在一个没有FirstOrDefault 的查询中完成...
  • 这是我第一次尝试使调用同步(来自 Functional Alchemy 的想法:使 Silverlight 同步)
  • Rx 就是异步的。如果您尝试使其同步,您很可能会遇到死锁。您使用“LinqPad”工具吗?如果没有,你应该下载它。非常适合在“便签本”环境中尝试 Rx 代码。
  • 它声明“Rx 是一个库,用于使用可观察序列和 LINQ 样式的查询运算符组合异步和基于事件的程序”。我正在使用 Rx 将我的事件作为可观察的集合进行访问,因此利用了我们今天拥有的 Linq 优势。

标签: .net csv webclient system.reactive


【解决方案1】:

很难知道你的目标是什么(我认为你试图简化在 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 类中,而不是在可观察的流。

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

【讨论】:

  • 感谢您提供的解决方案和周到的评论。为此,我已将您的答案标记为有用。它还不是我想要同步变化的完整解决方案。你能补充一下吗?
【解决方案2】:

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

【讨论】:

  • 这篇文章所做的就是将现有答案放入 WPF 窗口的代码中。 OP 根本没有询问 WPF,这增加了与所问问题相关的 nothing
  • 没错,但这就是帖子的标题所表明的“WPF 的改进答案”我这样做是为了帮助在 WPF 领域遇到同样问题的其他人(包括我自己)!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-17
  • 1970-01-01
相关资源
最近更新 更多