简体   繁体   English

使MEF插件与Rx一起使用

[英]Making MEF plugins work with Rx

So the current situation is I have a program that is completely utilizing MEF. 所以目前的情况是我有一个完全利用MEF的程序。 Now I want to make it utilize Rx as to allow it to scale to larger queries and allow the user to look over results as the various plugins return results. 现在我想让它利用Rx,允许它扩展到更大的查询,并允许用户在各种插件返回结果时查看结果。 It is currently setup as such: 它目前设置如下:

Workflow: Query => DetermineTypes => QueryPlugins => Results 工作流程: Query => DetermineTypes => QueryPlugins => Results

Currently the code is all stored on GitHub if anyone needs to reference more than what I post below. 目前,如果有人需要引用的内容超过我在下面发布的内容,则代码全部存储在GitHub上。 ALeRT on GitHub 关于GitHub的ALeRT

With the help of @Enigmativity the primary portion is now running with Rx. 在@Enigmativity的帮助下,主要部分现在与Rx一起运行。 Now when I got that done, I thought I had the Framework setup to handle System.IObservable<string> by utilizing var.ToObservable() . 现在,当我完成这项工作后,我认为我已经通过利用var.ToObservable()来设置框架来处理System.IObservable<string> This sadly doesn't seem to work (at least not as I have it). 遗憾的是,这似乎不起作用(至少不像我有的那样)。 The plugin framework is currently setup as such: 插件框架目前设置如下:

public interface IQueryPlugin
{
    string PluginCategory { get; }
    string Name { get; }
    string Version { get; }
    string Author { get; }
    System.Collections.Generic.List<string> TypesAccepted { get; }
    System.IObservable<string> Result(string input, string type, bool sensitive);
}

One example plugin Result method that I was trying to fix, but failed looks as such: 我尝试修复的一个示例插件Result方法,但看起来失败了:

public System.IObservable<string> Result(string input, string type, bool sensitive)
{

    string csv = "\"Roundtrip Time\"," + "\"Status\"\n";

    if (sensitive == true)
    {
        csv += "\"" + "" + "\"," + "\"" + "FORBIDDEN" + "\"\n";
    }
    else
    {
        if (type == "URL")
        {
            input = new Uri(input).Host;
        }
        Ping ping = new Ping();
        PingReply pingReply = ping.Send(input);

        csv += "\"" + pingReply.RoundtripTime.ToString() + "\"," + "\"" + pingReply.Status.ToString() + "\"\n";
    }

    return csv.ToObservable();
}

This naturally provides the following error: Cannot implicitly convert type System.IObservable<char> to System.IObservable<string> . 这自然会提供以下错误: 无法将System.IObservable<char>类型隐式转换为System.IObservable<string>

So the question is what is the best way to pass the data from a plugin to the main program. 所以问题是将数据从插件传递到主程序的最佳方法是什么。 I can handle switching types if it benefits the situation and keeps the plugin interface relatively simple. 如果它有利于这种情况并且保持插件界面相对简单,我可以处理切换类型。 The goal is to keep the plugin as simple as possible for any users who write their own. 目标是为任何自己编写的用户保持插件尽可能简单。

And for a point of completion, I'll drop the whole MainWindow.xaml.cs below to see how it is all currently setup. 至于完成点,我将删除下面的整个MainWindow.xaml.cs,看看它是如何设置的。

using ALeRT.PluginFramework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using GenericParsing;
using System.Windows.Markup;
using System.Data;
using System.Reactive.Linq;

namespace ALeRT.UI
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new DirectoryCatalog(Directory.GetCurrentDirectory()));
            var container = new CompositionContainer(catalog);

            try
            {
                container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                MessageBox.Show(compositionException.ToString());
            }

            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void queryButton_Click(object sender, RoutedEventArgs e)
        {
            string line;

            //resultDS.Reset(); //Looking for a way to clear our the contents from last time without breaking SelectionChanged

            if (File.Exists(queryTB.Text) && (bool)listCB.IsChecked)
            {
                StreamReader file = null;
                try
                {
                    file = new StreamReader(queryTB.Text);
                    while ((line = file.ReadLine()) != null)
                    {
                        QueryPlugins(line, DetermineTypes(line), (bool)sensitiveCB.IsChecked);
                    }
                }
                finally
                {
                    if (file != null) { file.Close(); }
                }
            }
            else
            {
                QueryPlugins(queryTB.Text, DetermineTypes(queryTB.Text), (bool)sensitiveCB.IsChecked);
            }
        }

        DataSet resultsDS = new DataSet("Results");

        [ImportMany]
        public IEnumerable<ITypePlugin> TPlugins { get; set; }

        [ImportMany]
        public IEnumerable<IQueryPlugin> QPlugins { get; set; }

        /// <summary>
        /// Method to process all Type plugins.
        /// </summary>
        private List<string> DetermineTypes(string val)
        {
            List<string> typeResultAL = new List<string>();

            foreach (var tPlugins in this.TPlugins)
            {
                if (tPlugins.Result(val))
                {
                    typeResultAL.Add(tPlugins.Name);
                }
            }
            return typeResultAL;
        }

        /// <summary>
        /// Method to process all Query plugins.
        /// </summary>
        private void QueryPlugins(string query, List<string> types, bool sensitive)
        {
            foreach (string tType in types) //Cycle through a List<string>
            {
                foreach (var qPlugins in this.QPlugins) //Cycle through all query plugins
                {
                    foreach (string qType in qPlugins.TypesAccepted)  //Cycle though a List<string> within the IQueryPlugin interface AcceptedTypes
                    {
                        if (qType == tType) //Match the two List<strings>, one is the AcceptedTypes and the other is the one returned from ITypeQuery
                        {
                            IObservable<DataTable> q =
                                from text in qPlugins.Result(query, qType, sensitive)
                                from tempTable in Observable.Using(
                                () => new GenericParserAdapter(),
                                parser => Observable.Using(() => new StringReader(text),
                                    sr => Observable.Start<DataTable>(
                                        () =>
                                        {
                                            var rNum = new Random();
                                            parser.SetDataSource(sr);
                                            parser.ColumnDelimiter = Convert.ToChar(",");
                                            parser.FirstRowHasHeader = true;
                                            parser.MaxBufferSize = 4096;
                                            parser.MaxRows = 500;
                                            parser.TextQualifier = '\"';

                                            var tempTable = parser.GetDataTable();
                                            tempTable.TableName = qPlugins.Name.ToString();
                                            if (!tempTable.Columns.Contains("Query"))
                                            {
                                                DataColumn tColumn = new DataColumn("Query");
                                                tempTable.Columns.Add(tColumn);
                                                tColumn.SetOrdinal(0);
                                            }

                                            foreach (DataRow dr in tempTable.Rows)
                                                dr["Query"] = query;

                                            return tempTable;
                                        }
                                        )))
                                select tempTable;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Open a dialog prompt to select a file to process.
        /// </summary>
        private void browseButton_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.Filter = "All Files|*.*";

            Nullable<bool> result = dlg.ShowDialog();

            if (result == true)
            {
                queryTB.Text = dlg.FileName;
            }
        }

        private void pluginsLB_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            resultsDG.ItemsSource = resultsDS.Tables[pluginsLB.SelectedValue.ToString()].DefaultView;
        }
    }
}

Here's how I would write the Result method: 以下是我将如何编写Result方法:

public System.IObservable<string> Result(
    string input, string type, bool sensitive)
{
    return Observable.Start(() =>
    {
        var csv = "\"Roundtrip Time\",\"Status\"\n";

        if (sensitive == true)
        {
            csv += "\"\",\"FORBIDDEN\"\n";
        }
        else
        {
            var input2 = type == "URL" ? new Uri(input).Host : input;
            ver ping = new Ping();
            ver pingReply = ping.Send(input2);

            csv += String.Format("\"{0}\",\"{1}\"\n",
                pingReply.RoundtripTime, pingReply.Status);
        }

        return csv;
    });
}

The best way to write code for your plugin with Rx is to make sure that you stay within the observables for as much as possible - make your data into observables early in their life and only come out of observables as late in their life as possible. 使用Rx为您的插件编写代码的最佳方法是确保尽可能多地保留在可观察的内容中 - 在生命的早期将数据变为可观察的数据,并且尽可能在生命的后期出现在可观察的数据中。 Then everything should fit together nicely. 然后一切都应该很好地融合在一起。

Does this help? 这有帮助吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM