我想要构建一个类似于管道的东西,在这里我可以放置命令(转换器、读者、作者.)在一起,就像一个线性工作流,并通过给定的命令参数将它们连接起来。一切都应该是可序列化的,一个干净的xml是必须的,因为在第一步中没有计划图形编辑器,所以对xml进行了编辑(在VS中支持生成的模式和intellisense,这并不痛苦)。
样本:
<DataCommand name="AboFileReader">
<connection>
<parameter name="filename" direction="in" type="string">
<parameter name="tabledata" direction="out" type="DataTable">
</connection>
</DataCommand>
<DataCommand name="TrimConverter">
<connection>
<param name="tabledata" direction="in" type="DataTable" >
<param name="tabledata" direction="out" type="DataTable" >
</connection>
</DataCommand>
<DataCommand name="AboDataConverter">
<connection>
<param name="tabledata" direction="in" type="DataTable" >
<param name="tabledata" direction="out" type="DataTable" >
</connection>
</DataCommand>
<DataCommand name="AboSqlWriter">
<connection>
<param name="tabledata" direction="in" type="DataTable" >
</connection>
</DataCommand>我想到了三种不同的方法,它们都有优点和缺点,我不知道该选择哪一种。
首先,我认为1是要走的路,但后来我想出了解决方案3。因为它提供了一个干净的xml,并且是本机可序列化的。对于下一步(编辑器),绑定到GUI应该很容易,并且属性网格进行反射并不是我的第一选择(反射总是丑陋的),拥有一组参数可以轻松地绑定到GUI。
也许1和3的组合是在启动时收集集合中所有参数的方法吗?
1.数据注释
[Parameter(Direction = "In", Description="...")]
public string FileName {get; set;}
[Parameter(Direction = "Out", Description="...")]
public DataTable Table {get; set;}优点:+本机参数在编码时公开+智能感知
缺点:-原生序列化?-丑陋的xml?
2. Workflow样式
public InParameter<string> FileName {get; set;}
public OutParameter<DataTable> Table {get; set;}优点:+编码+参数公开时的智能感知+序列化工作
缺点:-丑陋的xml?
3.填充构造函数的集合
ObservableCollection<Parameter> Parameters {get; set;}
public Init()
{
Parameters.Add( new Parameter() { Name="FileName", Type = typeOf(string), Direction="In", Description="..." } );
Parameters.Add( new Parameter() { Name="Table", Type = typeOf(DataTable), Direction="Out", Description="..." } );
}优点:+序列化作品+干净的xml
缺点:-在源代码中没有公开参数-编码时没有智能感知
你有什么经验和路要走?
发布于 2014-09-15 17:35:38
就我个人而言,我会选择一种不那么冗长的XML表示形式,比如:
<ReadFile filename="..." />
<Trim />
<Convert />
<WriteSQL />我假设大多数操作都有特定的输入和输出类型,所以除非这应该是可配置的,否则没有必要详细说明。这消除了一堆属性,但不是全部,所以您的问题仍然存在。理想情况下,您需要的东西是:
您想要使用属性是因为1号,因为第二个属性,您需要自动确定哪些属性需要序列化--这就是反射出现的地方。因为第三个原因,您只想序列化那些可配置的字段,所以您需要一些东西来区分这些属性。
使用属性来指示要公开哪些属性是一个好主意,因为它清楚地传达了它们的意图。它还允许您忽略其他属性,这使序列化格式保持干净。您还可以选择只序列化公共属性,但属性提供了更大的灵活性。最坏的情况是,它们会使代码更加混乱。
下面是我设置基本知识的方法:
public interface IOperation
{
Type InputType { get; }
Type OutputType { get; }
object GetOutput(object input);
}每个操作都提供了自己的IOperation接口实现:
public class ReadFileOperation : IOperation
{
[Parameter]
public string Filename { get; set; }
public Type InputType { get { return null; } } // No input
public Type OutputType { get { return typeof(DataTable); } }
public object GetOutput(object input)
{
// TODO: Read file into DataTable here!
}
}如果您确实有可以配置为使用不同输入或输出类型的操作,那么给InputType或OutputType一个setter和一个[Parameter]属性应该可以很好地工作。
您可以编写一个函数,查找实现IOperation的所有类型,并为每个类型存储属性列表。然后,在解析或写入时,查找当前类型并使用其属性列表。这个函数只需要调用一次,如果性能确实有问题,您可以在应用程序启动时使用它的输出来生成解析/编写代码,但除非我真的必须这样做,否则我不会这样做。
public Dictionary<Type, List<PropertyInfo>> DetectParameterProperties()
{
Dictionary<Type, List<PropertyInfo>> lookup = new Dictionary<Type, List<PropertyInfo>>();
IEnumerable<Type> operations = Assembly
.GetAssembly(typeof(IOperation))
.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && typeof(IOperation).IsAssignableFrom(t));
foreach (Type operation in operations)
{
lookup[operation] = new List<PropertyInfo>();
IEnumerable<PropertyInfo> parameters = operation.GetProperties().Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(ParameterAttribute)));
foreach (PropertyInfo parameter in parameters)
lookup[operation].Add(parameter);
}
return lookup;
}请注意,随着您提供更多的配置选项,错误处理变得更加重要。一个简单的类型检查,以确保所有输入和输出类型匹配良好,但一些操作可能需要自己的检查,以确保它们不会以其他方式被错误配置。
https://stackoverflow.com/questions/25851481
复制相似问题