所以目前的情况是我有一个完全利用MEF的程序。现在,我想让它利用Rx来扩展到更大的查询,并允许用户在各种插件返回结果时查看结果。目前设置如下:
工作流:查询=>确定类型=>查询插件=>结果
目前,如果有人需要引用比我下面发布的内容更多的内容,这些代码都存储在GitHub上。GitHub 上的ALeRT
在@Enigmatity的帮助下,主要部分现在与Rx一起运行。现在,当我完成这项工作时,我认为我已经具备了通过使用var.ToObservable()
来处理System.IObservable<string>
的框架设置。遗憾的是,这似乎不起作用(至少不像我所说的那样)。插件框架目前设置如下:
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方法,但失败了:
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>
。
因此,问题是将数据从插件传递到主程序的最佳方式是什么。我可以处理切换类型,如果它有利于这种情况,并保持插件接口相对简单。目标是让任何编写自己插件的用户都能尽可能简单地使用插件。
为了完成这一点,我将把整个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;
}
}
}
以下是我如何编写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;
});
}
使用Rx为插件编写代码的最佳方法是确保尽可能多地保持在可观察性范围内——在其生命的早期将数据放入可观察器中,并尽可能晚地从可观察物中取出。那么一切都应该很好地结合在一起。
这有帮助吗?