使MEF插件与Rx一起使用 [英] Making MEF plugins work with Rx

查看:111
本文介绍了使MEF插件与Rx一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以目前的情况是我有一个完全利用MEF的程序.现在,我想使其利用Rx来扩展到更大的查询,并允许用户在各种插件返回结果时查看结果.当前是这样设置的:

So the current situation is I have a program that is completely utilizing 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. It is currently setup as such:

工作流程:查询=>确定类型=>查询插件=>结果

Workflow: Query => DetermineTypes => QueryPlugins => Results

当前,如果有人需要引用的内容超过我在下面发布的内容,则代码全部存储在GitHub上. GitHub上的ALeRT

Currently the code is all stored on GitHub if anyone needs to reference more than what I post below. ALeRT on GitHub

借助@Enigmativity,主要部分是现在现在,当我完成该工作时,我以为我具有使用var.ToObservable()来处理System.IObservable<string>的Framework设置.可悲的是,这似乎不起作用(至少不如我所愿).插件框架当前的设置如下:

With the help of @Enigmativity the primary portion is now running with Rx. Now when I got that done, I thought I had the Framework setup to handle System.IObservable<string> by utilizing var.ToObservable(). 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);
}

我尝试修复的一个示例插件Result方法,但失败了,像这样:

One example plugin Result method that I was trying to fix, but failed looks as such:

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

这自然会产生以下错误:无法将类型System.IObservable<char>隐式转换为System.IObservable<string> .

This naturally provides the following error: Cannot implicitly convert type System.IObservable<char> to 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.

为了完成一点,我将在下面放下整个MainWindow.xaml.cs,以查看当前的所有设置.

And for a point of completion, I'll drop the whole MainWindow.xaml.cs below to see how it is all currently setup.

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

推荐答案

这是我编写Result方法的方式:

Here's how I would write the Result method:

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

使用Rx为您的插件编写代码的最好方法是确保您尽可能多地停留在可观察对象之内-使数据在它们的生命早期就进入可观察对象,而仅在它们的生命后期才出现在可观察对象中尽可能.然后一切都应该很好地融合在一起.

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. Then everything should fit together nicely.

有帮助吗?

这篇关于使MEF插件与Rx一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆